diff options
| author | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-10-04 09:25:36 +0200 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-10-04 09:25:36 +0200 |
| commit | 4cfbd259d1a6fbe7592b8975eed399b46082edc1 (patch) | |
| tree | c6095f5d2e0f1eabae41ddb62a6adc74d2210f97 /src/import_service.cpp | |
| parent | 485e5ebf340857db07b1c8ecb5c63dcf3a908377 (diff) | |
import ui
Diffstat (limited to 'src/import_service.cpp')
| -rw-r--r-- | src/import_service.cpp | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/import_service.cpp b/src/import_service.cpp new file mode 100644 index 0000000..b7a519c --- /dev/null +++ b/src/import_service.cpp @@ -0,0 +1,152 @@ +/* +* Copyright (c) 2025 Aldrik Ramaekers <aldrik.ramaekers@gmail.com> +* +* Permission to use, copy, modify, and/or distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#define _CRT_SECURE_NO_WARNINGS + +#include <fstream> +#include <iostream> +#include <string> +#include <threads.h> + +#define CPPHTTPLIB_OPENSSL_SUPPORT +#include "httplib.h" +#include "log.hpp" +#include "import_service.hpp" +#include "strops.hpp" +#include "administration_reader.hpp" +#include "locales.hpp" + +ai_provider_impl _ai_get_impl() +{ + ai_provider provider = administration_get_ai_service().provider; + + switch(provider) + { + case AI_PROVIDER_OPENAI: return _chatgpt_api_provider; + default: assert(0); break; + } + + return ai_provider_impl {0}; +} + +extern const char* peppol_invoice_template; +extern const char* peppol_invoice_line_template; + +static int _ai_document_to_invoice_t(void *arg) { + import_invoice_request* request = (import_invoice_request*)arg; + char* file_path = request->file_path; + ai_provider_impl impl = _ai_get_impl(); + + request->status = import_status::IMPORT_UPLOADING_FILE; + + char file_id[100]; + if (!impl.upload_file(file_path, file_id, 100)) { + request->status = import_status::IMPORT_DONE; + request->error = I_ERR_FAILED_UPLOAD; + return 0; + } + + request->status = import_status::IMPORT_QUERYING; + + size_t query_buffer_len = 50000; + char* template_buffer = (char*)malloc(query_buffer_len); + memset(template_buffer, 0, query_buffer_len); + + strncpy(template_buffer, peppol_invoice_template, query_buffer_len); + strops_replace(template_buffer, 50000, "{{INVOICE_LINE_LIST}}", peppol_invoice_line_template); + + char* ai_query = + "\n\nI have provided a file containing an invoice. Fill in the above Peppol 3.0 template with the information from the invoice." + "Do not add any fields to the template. If you can't find data for a given field, leave it empty. Do not make up any information." + "Only return the filled out template in valid XML format. Nothing else.\n"; + + size_t query_len = strlen(template_buffer); + strncpy(template_buffer + query_len, ai_query, query_buffer_len - query_len); + + request->status = import_status::IMPORT_WAITING_FOR_RESPONSE; + + char* response; + if (!impl.query_with_file(template_buffer, query_buffer_len, file_id, &response)) { + request->status = import_status::IMPORT_DONE; + request->error = I_ERR_FAILED_QUERY; + return 0; + } + + invoice inv; + if (!administration_reader_read_invoice_from_xml(&inv, response, strlen(response))) { + request->status = import_status::IMPORT_DONE; + request->error = I_ERR_FAILED_IMPORT; + return 0; + } + + invoice tmp = administration_invoice_create_empty(); + + inv.status = invoice_status::INVOICE_RECEIVED; + strops_copy(inv.id, tmp.id, MAX_LEN_ID); // TODO next_id is not being incremented + contact my_info = administration_company_info_get(); + memcpy(&inv.customer, &my_info, sizeof(contact)); + strops_copy(inv.customer.id, MY_COMPANY_ID, MAX_LEN_ID); + + strops_copy(inv.document.original_path, file_path, MAX_LEN_PATH); + strops_copy(inv.document.copy_path, "", MAX_LEN_PATH); + + free(template_buffer); + free(response); + + request->status = import_status::IMPORT_DONE; + request->result = administration_invoice_create_copy(&inv); + return 0; +} + +import_invoice_request* ai_document_to_invoice(char* file_path) +{ + import_invoice_request* result = (import_invoice_request*)malloc(sizeof(import_invoice_request)); + result->started_at = time(NULL); + result->error = I_ERR_SUCCESS; + result->status = import_status::IMPORT_STARTING; + strops_copy(result->file_path, file_path, MAX_LEN_PATH); + + thrd_t thr; + if (thrd_create(&thr, _ai_document_to_invoice_t, result) != thrd_success) { + return 0; + } + + return result; +} + +const char* import_status_to_str(import_status status) +{ + switch(status) + { + case import_status::IMPORT_STARTING: return localize("import.status.starting"); + case import_status::IMPORT_UPLOADING_FILE: return localize("import.status.uploading_file"); + case import_status::IMPORT_QUERYING: return localize("import.status.querying"); + case import_status::IMPORT_WAITING_FOR_RESPONSE: return localize("import.status.waiting_for_result"); + case import_status::IMPORT_DONE: return localize("import.status.done"); + } + return ""; +} + +const char* import_error_to_str(i_err error) +{ + switch(error) + { + case I_ERR_FAILED_UPLOAD: return localize("import.error.upload"); + case I_ERR_FAILED_QUERY: return localize("import.error.query"); + case I_ERR_FAILED_IMPORT: return localize("import.error.import"); + } + return ""; +}
\ No newline at end of file |
