summaryrefslogtreecommitdiff
path: root/src/import_service.cpp
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-10-04 09:25:36 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-10-04 09:25:36 +0200
commit4cfbd259d1a6fbe7592b8975eed399b46082edc1 (patch)
treec6095f5d2e0f1eabae41ddb62a6adc74d2210f97 /src/import_service.cpp
parent485e5ebf340857db07b1c8ecb5c63dcf3a908377 (diff)
import ui
Diffstat (limited to 'src/import_service.cpp')
-rw-r--r--src/import_service.cpp152
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