summaryrefslogtreecommitdiff
path: root/src
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
parent485e5ebf340857db07b1c8ecb5c63dcf3a908377 (diff)
import ui
Diffstat (limited to 'src')
-rw-r--r--src/ai_providers/openAI.cpp20
-rw-r--r--src/import_service.cpp (renamed from src/ai_service.cpp)71
-rw-r--r--src/locales/en.cpp11
-rw-r--r--src/main.cpp2
-rw-r--r--src/ui/ui_expenses.cpp61
5 files changed, 144 insertions, 21 deletions
diff --git a/src/ai_providers/openAI.cpp b/src/ai_providers/openAI.cpp
index e005a80..7675e8b 100644
--- a/src/ai_providers/openAI.cpp
+++ b/src/ai_providers/openAI.cpp
@@ -24,15 +24,13 @@
#include "httplib.h"
#include "strops.hpp"
#include "log.hpp"
-#include "ai_service.hpp"
+#include "import_service.hpp"
static bool _openAI_query_with_file(char* query, size_t query_length, char* file_id, char** response)
{
- //#define TESTING_IMPORT
-
- #ifndef TESTING_IMPORT
+ #if 0
const char *api_key = administration_get_ai_service().api_key_public;
-
+
httplib::SSLClient cli("api.openai.com", 443);
//cli.enable_server_certificate_verification(false);
@@ -57,16 +55,17 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file
}
char* response_body = (char*)res->body.c_str();
- #else
- char* response_body = "{\n \"id\": \"resp_68d9482030fc8196930b43b6b28feeb104e98afee829eee0\",\n \"object\": \"response\",\n \"created_at\": 1759070240,\n \"status\": \"completed\",\n \"background\": false,\n \"billing\": {\n \"payer\": \"developer\"\n },\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"max_tool_calls\": null,\n \"model\": \"gpt-5-2025-08-07\",\n \"output\": [\n {\n \"id\": \"rs_68d94821d1f0819694533a6ed7ed6b2904e98afee829eee0\",\n \"type\": \"reasoning\",\n \"summary\": []\n },\n {\n \"id\": \"msg_68d948a09e0c819696782e09c6b7626104e98afee829eee0\",\n \"type\": \"message\",\n \"status\": \"completed\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"annotations\": [],\n \"logprobs\": [],\n \"text\": \"<Invoice xmlns=\\\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\\\" xmlns:cac=\\\"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\\\" xmlns:cbc=\\\"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\\\">\\n <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>\\n <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>\\n <cbc:ID>586928</cbc:ID>\\n <cbc:IssueDate>2025-03-17</cbc:IssueDate>\\n <cbc:DueDate>2025-03-24</cbc:DueDate>\\n <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>\\n <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>\\n <cac:DespatchDocumentReference>\\n <cbc:ID>699607</cbc:ID>\\n </cac:DespatchDocumentReference>\\n <cac:AdditionalDocumentReference>\\n <cbc:ID>AR385893</cbc:ID>\\n <cbc:DocumentDescription>Jouw bestelling : 420675-WWW.SCHROEVEN-EXPRESS.NL-14.03.25-18:31</cbc:DocumentDescription>\\n </cac:AdditionalDocumentReference>\\n <cac:OrderReference>\\n <cbc:ID>420675-WWW.SCHROEVEN-EXPRESS.NL-14.03.25-18:31</cbc:ID>\\n </cac:OrderReference>\\n <cac:ProjectReference>\\n <cbc:ID></cbc:ID>\\n </cac:ProjectReference>\\n <cbc:AccountingCost></cbc:AccountingCost>\\n <cac:AccountingSupplierParty>\\n <cac:Party>\\n <cbc:EndpointID schemeID=\\\"\\\"></cbc:EndpointID>\\n <cac:PartyIdentification>\\n <cbc:ID schemeID=\\\"ZZZ\\\">R.C le mans B 302 494 224</cbc:ID>\\n </cac:PartyIdentification>\\n <cac:PartyName>\\n <cbc:Name>Visserie-service</cbc:Name>\\n </cac:PartyName>\\n <cac:PostalAddress>\\n <cbc:StreetName>Z.A Nord</cbc:StreetName>\\n <cbc:AdditionalStreetName></cbc:AdditionalStreetName>\\n <cbc:CityName>Parce sur Sarthe</cbc:CityName>\\n <cbc:PostalZone>72300</cbc:PostalZone>\\n <cbc:CountrySubentity></cbc:CountrySubentity>\\n <cac:Country>\\n <cbc:IdentificationCode>FR</cbc:IdentificationCode>\\n </cac:Country>\\n </cac:PostalAddress>\\n <cac:PartyTaxScheme>\\n <cbc:CompanyID>FR57 302 494 224</cbc:CompanyID>\\n <cac:TaxScheme>\\n <cbc:ID>VAT</cbc:ID>\\n </cac:TaxScheme>\\n </cac:PartyTaxScheme>\\n <cac:PartyLegalEntity>\\n <cbc:RegistrationName>Visserie Service SAS</cbc:RegistrationName>\\n </cac:PartyLegalEntity>\\n <cac:Contact>\\n <cbc:Name>AMELIE L</cbc:Name>\\n <cbc:Telephone>02.43.62.09.08</cbc:Telephone>\\n <cbc:ElectronicMail>klantenservice@schroeven-express.nl</cbc:ElectronicMail>\\n </cac:Contact>\\n </cac:Party>\\n </cac:AccountingSupplierParty>\\n <cac:AccountingCustomerParty>\\n <cac:Party>\\n <cbc:EndpointID schemeID=\\\"\\\"></cbc:EndpointID>\\n <cac:PartyIdentification>\\n <cbc:ID schemeID=\\\"ZZZ\\\">cl585187</cbc:ID>\\n </cac:PartyIdentification>\\n <cac:PartyName>\\n <cbc:Name>ALDRIK RAMAEKERS</cbc:Name>\\n </cac:PartyName>\\n <cac:PostalAddress>\\n <cbc:StreetName>KEERDERSTRAAT 81</cbc:StreetName>\\n <cbc:AdditionalStreetName></cbc:AdditionalStreetName>\\n <cbc:CityName>MAASTRICHT</cbc:CityName>\\n <cbc:PostalZone>6226X</cbc:PostalZone>\\n <cbc:CountrySubentity></cbc:CountrySubentity>\\n <cac:Country>\\n <cbc:IdentificationCode>NL</cbc:IdentificationCode>\\n </cac:Country>\\n </cac:PostalAddress>\\n <cac:PartyTaxScheme>\\n <cbc:CompanyID></cbc:CompanyID>\\n <cac:TaxScheme>\\n <cbc:ID>VAT</cbc:ID>\\n </cac:TaxScheme>\\n </cac:PartyTaxScheme>\\n <cac:PartyLegalEntity>\\n <cbc:RegistrationName>ALDRIK RAMAEKERS</cbc:RegistrationName>\\n </cac:PartyLegalEntity>\\n <cac:Contact>\\n <cbc:Name>A RAMAEKERS</cbc:Name>\\n <cbc:Telephone>31618260377</cbc:Telephone>\\n <cbc:ElectronicMail>aldrikboy@gmail.com</cbc:ElectronicMail>\\n </cac:Contact>\\n </cac:Party>\\n </cac:AccountingCustomerParty>\\n <cac:Delivery>\\n <cbc:ActualDeliveryDate>2025-03-17</cbc:ActualDeliveryDate>\\n <cac:DeliveryLocation>\\n <cac:Address>\\n <cbc:StreetName>KEERDERSTRAAT 81</cbc:StreetName>\\n <cbc:AdditionalStreetName></cbc:AdditionalStreetName>\\n <cbc:CityName>MAASTRICHT</cbc:CityName>\\n <cbc:PostalZone>6226X</cbc:PostalZone>\\n <cbc:CountrySubentity></cbc:CountrySubentity>\\n <cac:Country>\\n <cbc:IdentificationCode>NL</cbc:IdentificationCode>\\n </cac:Country>\\n </cac:Address>\\n </cac:DeliveryLocation>\\n <cac:DeliveryParty>\\n <cac:PartyName>\\n <cbc:Name>ALDRIK RAMAEKERS</cbc:Name>\\n </cac:PartyName>\\n </cac:DeliveryParty>\\n </cac:Delivery>\\n <cac:PaymentMeans>\\n <cbc:PaymentMeansCode></cbc:PaymentMeansCode>\\n <cbc:PaymentID>586928</cbc:PaymentID>\\n <cac:PayeeFinancialAccount>\\n <cbc:ID>FR76 1790 6001 1272 5017 0700 137</cbc:ID>\\n <cbc:Name>Visserie Service SAS</cbc:Name>\\n <cac:FinancialInstitutionBranch>\\n <cac:FinancialInstitution>\\n <cbc:ID>AGRIFRPP879</cbc:ID>\\n </cac:FinancialInstitution>\\n </cac:FinancialInstitutionBranch>\\n </cac:PayeeFinancialAccount>\\n <cac:PayerFinancialAccount>\\n <cbc:ID></cbc:ID>\\n </cac:PayerFinancialAccount>\\n </cac:PaymentMeans>\\n <cac:TaxTotal>\\n <cbc:TaxAmount currencyID=\\\"EUR\\\">2.59</cbc:TaxAmount>\\n <cac:TaxSubtotal>\\n <cbc:TaxableAmount currencyID=\\\"EUR\\\">12.36</cbc:TaxableAmount>\\n <cbc:TaxAmount currencyID=\\\"EUR\\\">2.59</cbc:TaxAmount>\\n <cac:TaxCategory>\\n <cbc:ID></cbc:ID>\\n <cbc:Percent>21</cbc:Percent>\\n <cac:TaxScheme>\\n <cbc:ID>VAT</cbc:ID>\\n </cac:TaxScheme>\\n </cac:TaxCategory>\\n </cac:TaxSubtotal>\\n </cac:TaxTotal>\\n <cac:LegalMonetaryTotal>\\n <cbc:LineExtensionAmount currencyID=\\\"EUR\\\">6.95</cbc:LineExtensionAmount>\\n <cbc:TaxExclusiveAmount currencyID=\\\"EUR\\\">12.36</cbc:TaxExclusiveAmount>\\n <cbc:TaxInclusiveAmount currencyID=\\\"EUR\\\">14.95</cbc:TaxInclusiveAmount>\\n <cbc:PayableAmount currencyID=\\\"EUR\\\">14.95</cbc:PayableAmount>\\n </cac:LegalMonetaryTotal>\\n <cac:InvoiceLine>\\n <cbc:ID>1</cbc:ID>\\n <cbc:InvoicedQuantity unitCode=\\\"\\\">500</cbc:InvoicedQuantity>\\n <cbc:LineExtensionAmount currencyID=\\\"EUR\\\">6.95</cbc:LineExtensionAmount>\\n <cac:AllowanceCharge>\\n <cbc:ChargeIndicator>false</cbc:ChargeIndicator>\\n <cbc:AllowanceChargeReason>Discount</cbc:AllowanceChargeReason>\\n <cbc:MultiplierFactorNumeric></cbc:MultiplierFactorNumeric>\\n <cbc:Amount currencyID=\\\"EUR\\\"></cbc:Amount>\\n <cbc:BaseAmount currencyID=\\\"EUR\\\"></cbc:BaseAmount>\\n </cac:AllowanceCharge>\\n <cac:Item>\\n <cbc:Name>Metalen schroeven RVS A2 gefreesde kop Pozi N\\u00b01 M2X4 DIN 965 ISO 7046, VS0109, VS0110</cbc:Name>\\n <cac:AdditionalItemProperty>\\n <cbc:Name>Internal Tax Rate ID</cbc:Name>\\n <cbc:Value></cbc:Value>\\n </cac:AdditionalItemProperty>\\n <cac:ClassifiedTaxCategory>\\n <cbc:ID></cbc:ID>\\n <cbc:Percent>21</cbc:Percent>\\n <cac:TaxScheme>\\n <cbc:ID>VAT</cbc:ID>\\n </cac:TaxScheme>\\n </cac:ClassifiedTaxCategory>\\n </cac:Item>\\n <cac:Price>\\n <cbc:PriceAmount currencyID=\\\"EUR\\\">1.39</cbc:PriceAmount>\\n </cac:Price>\\n </cac:InvoiceLine>\\n</Invoice>\"\n }\n ],\n \"role\": \"assistant\"\n }\n ],\n \"parallel_tool_calls\": true,\n";
- #endif
-
*response = (char*)malloc(100000);
memset(*response, 0, 100000);
strncpy(*response, response_body, 100000);
strops_get_json_value(*response, "text", *response, 100000);
*response = strops_unprep_str_from_json(*response);
+ #else
+ *response = (char*)malloc(100000);
+ memset(*response, 0, 100000);
+ strops_copy(*response, "<Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\" xmlns:cac=\"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\" xmlns:cbc=\"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\"> <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID> <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID> <cbc:ID>492043632</cbc:ID> <cbc:IssueDate>2024-09-01</cbc:IssueDate> <cbc:DueDate>2024-09-01</cbc:DueDate> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cbc:DocumentCurrencyCode>USD</cbc:DocumentCurrencyCode> <cac:DespatchDocumentReference> <cbc:ID>Final invoice</cbc:ID> </cac:DespatchDocumentReference> <cac:AdditionalDocumentReference> <cbc:ID></cbc:ID> <cbc:DocumentDescription></cbc:DocumentDescription> </cac:AdditionalDocumentReference> <cac:OrderReference> <cbc:ID></cbc:ID> </cac:OrderReference> <cac:ProjectReference> <cbc:ID>do:team:67840ecb-44e2-472e-bc45-801bd4e1f1fe</cbc:ID> </cac:ProjectReference> <cbc:AccountingCost></cbc:AccountingCost> <cac:AccountingSupplierParty> <cac:Party> <cbc:EndpointID schemeID=""></cbc:EndpointID> <cac:PartyIdentification> <cbc:ID schemeID=\"ZZZ\"></cbc:ID> </cac:PartyIdentification> <cac:PartyName> <cbc:Name>DigitalOcean LLC</cbc:Name> </cac:PartyName> <cac:PostalAddress> <cbc:StreetName>101 Avenue of the Americas</cbc:StreetName> <cbc:AdditionalStreetName>2nd Floor</cbc:AdditionalStreetName> <cbc:CityName>New York</cbc:CityName> <cbc:PostalZone>10013</cbc:PostalZone> <cbc:CountrySubentity>NY</cbc:CountrySubentity> <cac:Country> <cbc:IdentificationCode>US</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyTaxScheme> <cbc:CompanyID>EU528002224</cbc:CompanyID> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:PartyTaxScheme> <cac:PartyLegalEntity> <cbc:RegistrationName>DigitalOcean LLC</cbc:RegistrationName> </cac:PartyLegalEntity> <cac:Contact> <cbc:Name></cbc:Name> <cbc:Telephone></cbc:Telephone> <cbc:ElectronicMail></cbc:ElectronicMail> </cac:Contact> </cac:Party> </cac:AccountingSupplierParty> <cac:AccountingCustomerParty> <cac:Party> <cbc:EndpointID schemeID=""></cbc:EndpointID> <cac:PartyIdentification> <cbc:ID schemeID=\"ZZZ\"></cbc:ID> </cac:PartyIdentification> <cac:PartyName> <cbc:Name>My Team</cbc:Name> </cac:PartyName> <cac:PostalAddress> <cbc:StreetName>Keerderstraat 81</cbc:StreetName> <cbc:AdditionalStreetName></cbc:AdditionalStreetName> <cbc:CityName>Maastricht</cbc:CityName> <cbc:PostalZone>6226 XW</cbc:PostalZone> <cbc:CountrySubentity>LI</cbc:CountrySubentity> <cac:Country> <cbc:IdentificationCode>NL</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyTaxScheme> <cbc:CompanyID></cbc:CompanyID> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:PartyTaxScheme> <cac:PartyLegalEntity> <cbc:RegistrationName></cbc:RegistrationName> </cac:PartyLegalEntity> <cac:Contact> <cbc:Name></cbc:Name> <cbc:Telephone></cbc:Telephone> <cbc:ElectronicMail>aldrikboy@gmail.com</cbc:ElectronicMail> </cac:Contact> </cac:Party> </cac:AccountingCustomerParty> <cac:Delivery> <cbc:ActualDeliveryDate></cbc:ActualDeliveryDate> <cac:DeliveryLocation> <cac:Address> <cbc:StreetName></cbc:StreetName> <cbc:AdditionalStreetName></cbc:AdditionalStreetName> <cbc:CityName></cbc:CityName> <cbc:PostalZone></cbc:PostalZone> <cbc:CountrySubentity></cbc:CountrySubentity> <cac:Country> <cbc:IdentificationCode></cbc:IdentificationCode> </cac:Country> </cac:Address> </cac:DeliveryLocation> <cac:DeliveryParty> <cac:PartyName> <cbc:Name></cbc:Name> </cac:PartyName> </cac:DeliveryParty> </cac:Delivery> <cac:PaymentMeans> <cbc:PaymentMeansCode></cbc:PaymentMeansCode> <cbc:PaymentID>492043632</cbc:PaymentID> <cac:PayeeFinancialAccount> <cbc:ID></cbc:ID> <cbc:Name></cbc:Name> <cac:FinancialInstitutionBranch> <cac:FinancialInstitution> <cbc:ID></cbc:ID> </cac:FinancialInstitution> </cac:FinancialInstitutionBranch> </cac:PayeeFinancialAccount> <cac:PayerFinancialAccount> <cbc:ID></cbc:ID> </cac:PayerFinancialAccount> </cac:PaymentMeans> <cac:TaxTotal> <cbc:TaxAmount currencyID=\"USD\">3.49</cbc:TaxAmount> <cac:TaxSubtotal> <cbc:TaxableAmount currencyID=\"USD\">15.60</cbc:TaxableAmount> <cbc:TaxAmount currencyID=\"USD\">3.28</cbc:TaxAmount> <cac:TaxCategory> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal> <cac:TaxSubtotal> <cbc:TaxableAmount currencyID=\"USD\">1.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID=\"USD\">0.21</cbc:TaxAmount> <cac:TaxCategory> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal> </cac:TaxTotal> <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID=\"USD\">16.60</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID=\"USD\">16.60</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID=\"USD\">20.09</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID=\"USD\">20.09</cbc:PayableAmount> </cac:LegalMonetaryTotal> <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode=""></cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID=\"USD\">16.60</cbc:LineExtensionAmount> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Discount</cbc:AllowanceChargeReason> <cbc:MultiplierFactorNumeric></cbc:MultiplierFactorNumeric> <cbc:Amount currencyID=\"USD\"></cbc:Amount> <cbc:BaseAmount currencyID=\"USD\"></cbc:BaseAmount> </cac:AllowanceCharge> <cac:Item> <cbc:Name>Product Usage Charges</cbc:Name> <cac:AdditionalItemProperty> <cbc:Name>Internal Tax Rate ID</cbc:Name> <cbc:Value></cbc:Value> </cac:AdditionalItemProperty> <cac:ClassifiedTaxCategory> <cbc:ID></cbc:ID> <cbc:Percent></cbc:Percent> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:ClassifiedTaxCategory> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID=\"USD\"></cbc:PriceAmount> </cac:Price> </cac:InvoiceLine></Invoice>", 100000);
+ #endif
return 1;
}
@@ -98,6 +97,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
httplib::Result res = cli.Post("/v1/uploads", headers, body, "application/json");
if (!res || res->status != 200) {
log_error("ERROR Failed to create upload.");
+ log_error(res->body.c_str());
fclose(orig_file);
return 0;
}
@@ -133,6 +133,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
if (!part_res || part_res->status != 200) {
log_error("Failed to upload part %d.", part_number);
+ log_error(part_res->body.c_str());
free(buffer);
fclose(orig_file);
return 0;
@@ -161,6 +162,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
if (!complete_res || complete_res->status != 200) {
log_error("ERROR Failed to complete upload.");
+ log_error(complete_res->body.c_str());
return 0;
}
diff --git a/src/ai_service.cpp b/src/import_service.cpp
index 82d81d6..b7a519c 100644
--- a/src/ai_service.cpp
+++ b/src/import_service.cpp
@@ -19,13 +19,15 @@
#include <fstream>
#include <iostream>
#include <string>
+#include <threads.h>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include "log.hpp"
-#include "ai_service.hpp"
+#include "import_service.hpp"
#include "strops.hpp"
#include "administration_reader.hpp"
+#include "locales.hpp"
ai_provider_impl _ai_get_impl()
{
@@ -43,15 +45,22 @@ ai_provider_impl _ai_get_impl()
extern const char* peppol_invoice_template;
extern const char* peppol_invoice_line_template;
-ai_request* ai_document_to_invoice(char* file_path)
-{
+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);
@@ -67,29 +76,77 @@ ai_request* ai_document_to_invoice(char* file_path)
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))) {
- return false;
+ 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
- strops_copy(inv.customer.id, MY_COMPANY_ID, MAX_LEN_ID); // TODO param for incomming/exporting necessary
+ 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);
- a_err result = administration_invoice_import(&inv);
-
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
diff --git a/src/locales/en.cpp b/src/locales/en.cpp
index 0152928..d311b36 100644
--- a/src/locales/en.cpp
+++ b/src/locales/en.cpp
@@ -196,6 +196,17 @@ locale_entry en_locales[] = {
{"statement.tax", "Tax"},
{"statement.expenses", "Expenses"},
{"statement.profit", "Profit"},
+
+ // Import service.
+ {"import.status.starting","Starting import"},
+ {"import.status.uploading_file","Uploading file"},
+ {"import.status.querying","Querying AI provider"},
+ {"import.status.waiting_for_result","Waiting for result"},
+ {"import.status.done","Import completed"},
+
+ {"import.error.upload","Failure: Upload failed"},
+ {"import.error.query","Failure: Querying service failed"},
+ {"import.error.import","Failure: Failed to import result from service"},
};
const int en_locale_count = sizeof(en_locales) / sizeof(en_locales[0]); \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index bdddd98..8475008 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -129,7 +129,7 @@ int main(int argc, char** argv)
io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\seguisym.ttf");
fontBold = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeuib.ttf");
- fontBig = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeuib.ttf", 36);
+ fontBig = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeuib.ttf", 30);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index 153e6b7..c02cdce 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -27,7 +27,9 @@
#include "administration.hpp"
#include "administration_writer.hpp"
#include "locales.hpp"
-#include "ai_service.hpp"
+#include "import_service.hpp"
+
+static import_invoice_request* active_import_request = 0;
static view_state current_view_state = view_state::LIST;
static invoice active_invoice = {0};
@@ -46,7 +48,13 @@ void ui_destroy_expenses()
void ui_setup_expenses()
{
- current_view_state = view_state::LIST;
+ if (active_import_request != 0) {
+ current_view_state = view_state::VIEW_IMPORT_REQUEST;
+ }
+ else {
+ current_view_state = view_state::LIST;
+ }
+
active_invoice = administration_invoice_create_empty();
u32 invoice_items_count = MAX_BILLING_ITEMS;
@@ -176,13 +184,13 @@ static void ui_draw_expenses_list()
char import_file_path[MAX_LEN_PATH] = {0};
ImGui::SameLine();
if (ImGui::FormInvoiceFileSelector("+ Import", import_file_path)) { // @localize
- //current_view_state = view_state::CREATE;
+ current_view_state = view_state::VIEW_IMPORT_REQUEST;
active_invoice = administration_invoice_create_empty(); // @leak
active_invoice.customer = administration_company_info_get();
active_invoice.is_outgoing = 0;
active_invoice.status = invoice_status::INVOICE_RECEIVED;
- ai_document_to_invoice(import_file_path);
+ active_import_request = ai_document_to_invoice(import_file_path);
}
if (current_page >= max_page-1) current_page = max_page-1;
@@ -336,6 +344,50 @@ static void ui_draw_expense_view()
draw_expense_form(&active_invoice, true);
}
+static void ui_draw_import_request()
+{
+ assert(active_import_request);
+
+ if (active_import_request->status == import_status::IMPORT_DONE) {
+ if (active_import_request->error == I_ERR_SUCCESS) {
+ active_invoice = active_import_request->result;
+ current_view_state = view_state::CREATE;
+ active_import_request = 0;
+ return;
+ }
+ else {
+ if (ImGui::Button(localize("form.back"))) {
+ current_view_state = view_state::LIST;
+ active_import_request = 0;
+ return;
+ }
+ }
+ }
+
+ ImGui::PushFont(fontBig);
+
+ ImVec2 windowSize = ImGui::GetWindowSize();
+ float radius = 60.0f;
+
+ const char* text = import_status_to_str(active_import_request->status);
+ if (active_import_request->error != I_ERR_SUCCESS) text = import_error_to_str(active_import_request->error);
+ ImVec2 textSize = ImGui::CalcTextSize(text);
+ ImGui::SetCursorPos(ImVec2((windowSize.x - textSize.x) * 0.5f,
+ (windowSize.y) * 0.5f - radius - 40.0f));
+ ImGui::Text(text);
+
+ if (active_import_request->error == I_ERR_SUCCESS) {
+ ImGui::SetCursorPos(ImVec2((windowSize.x - radius*2) * 0.5f,
+ (windowSize.y - radius*2) * 0.5f));
+
+ const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
+ const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
+ ImGui::LoadingIndicatorCircle("##loadingAnim", radius, bg, col, 10, 4.0f);
+ }
+
+ ImGui::PopFont();
+}
+
void ui_draw_expenses()
{
switch(current_view_state)
@@ -344,5 +396,6 @@ void ui_draw_expenses()
case view_state::CREATE: ui_draw_expense_create(); break;
case view_state::EDIT: ui_draw_expense_update(); break;
case view_state::VIEW: ui_draw_expense_view(); break;
+ case view_state::VIEW_IMPORT_REQUEST: ui_draw_import_request(); break;
}
} \ No newline at end of file