summaryrefslogtreecommitdiff
path: root/src/administration_writer.cpp
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-31 12:49:59 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-31 12:49:59 +0200
commitb970907ef53f1b8367285cbe7c2dc2a7fa47967f (patch)
tree73ec8747346393263e21ea72cffb6aa27d921c4a /src/administration_writer.cpp
parent9fcfc3215a4caaa32872abc9cfc6deeb86b9765c (diff)
invoice templateing work
Diffstat (limited to 'src/administration_writer.cpp')
-rw-r--r--src/administration_writer.cpp152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp
index 00543af..40fcd25 100644
--- a/src/administration_writer.cpp
+++ b/src/administration_writer.cpp
@@ -6,7 +6,9 @@
#include <threads.h>
#include "ui.hpp"
+#include "strops.hpp"
#include "administration_writer.hpp"
+#include "file_templates.hpp"
mtx_t _save_file_mutex;
@@ -80,6 +82,155 @@ static bool administration_writer_write_to_zip(char* entry_to_replace, char* ori
}
/////////////////////////////
+//// Invoices
+/////////////////////////////
+static char* administration_writer_get_eas_id_for_contact(contact contact)
+{
+ // https://docs.peppol.eu/poacc/billing/3.0/codelist/eas/
+ char* country_code = contact.address.country_code;
+
+ // Countries using tax identification numbers.
+ if (strcmp(country_code, "AT") == 0) return contact.taxid; // Austria
+ if (strcmp(country_code, "BE") == 0) return contact.taxid; // Belgium
+ if (strcmp(country_code, "BG") == 0) return contact.taxid; // Bulgaria
+ if (strcmp(country_code, "CY") == 0) return contact.taxid; // Cyprus
+ if (strcmp(country_code, "CZ") == 0) return contact.taxid; // Czech Republic
+ if (strcmp(country_code, "DE") == 0) return contact.taxid; // Germany
+ if (strcmp(country_code, "EE") == 0) return contact.taxid; // Estonia
+ if (strcmp(country_code, "FR") == 0) return contact.taxid; // France
+ if (strcmp(country_code, "GR") == 0) return contact.taxid; // Greece
+ if (strcmp(country_code, "HR") == 0) return contact.taxid; // Croatia
+ if (strcmp(country_code, "HU") == 0) return contact.taxid; // Hungary
+ if (strcmp(country_code, "IE") == 0) return contact.taxid; // Ireland
+ if (strcmp(country_code, "LU") == 0) return contact.taxid; // Luxembourg
+ if (strcmp(country_code, "LV") == 0) return contact.taxid; // Latvia
+ if (strcmp(country_code, "MT") == 0) return contact.taxid; // Malta
+ if (strcmp(country_code, "NL") == 0) return contact.taxid; // Netherlands
+ if (strcmp(country_code, "PL") == 0) return contact.taxid; // Poland
+ if (strcmp(country_code, "PT") == 0) return contact.taxid; // Portugal
+ if (strcmp(country_code, "RO") == 0) return contact.taxid; // Romania
+ if (strcmp(country_code, "SI") == 0) return contact.taxid; // Slovenia
+ if (strcmp(country_code, "SK") == 0) return contact.taxid; // Slovakia
+ if (strcmp(country_code, "ES") == 0) return contact.taxid; // Spain
+
+ // Countries using business identification numbers.
+ if (strcmp(country_code, "SE") == 0) return contact.businessid; // Sweden
+ if (strcmp(country_code, "LT") == 0) return contact.businessid; // Lithuania
+ if (strcmp(country_code, "IT") == 0) return contact.businessid; // Italy
+ if (strcmp(country_code, "FI") == 0) return contact.businessid; // Finland
+ if (strcmp(country_code, "DK") == 0) return contact.businessid; // Denmark
+ return NULL; // Unknown country code
+}
+
+static char* administration_writer_get_eas_scheme_for_address(address addr)
+{
+ // https://docs.peppol.eu/poacc/billing/3.0/codelist/eas/
+ char* country_code = addr.country_code;
+ if (strcmp(country_code, "AT") == 0) return "9914"; // Austria
+ if (strcmp(country_code, "BE") == 0) return "9925"; // Belgium
+ if (strcmp(country_code, "BG") == 0) return "9926"; // Bulgaria
+ if (strcmp(country_code, "CY") == 0) return "9928"; // Cyprus
+ if (strcmp(country_code, "CZ") == 0) return "9929"; // Czech Republic
+ if (strcmp(country_code, "DE") == 0) return "9930"; // Germany
+ if (strcmp(country_code, "DK") == 0) return "0096"; // Denmark
+ if (strcmp(country_code, "EE") == 0) return "9931"; // Estonia
+ if (strcmp(country_code, "FR") == 0) return "9957"; // France
+ if (strcmp(country_code, "GR") == 0) return "9933"; // Greece
+ if (strcmp(country_code, "HR") == 0) return "9934"; // Croatia
+ if (strcmp(country_code, "HU") == 0) return "9910"; // Hungary
+ if (strcmp(country_code, "IE") == 0) return "9935"; // Ireland
+ if (strcmp(country_code, "FI") == 0) return "0212"; // Finland
+ if (strcmp(country_code, "IT") == 0) return "0208"; // Italy
+ if (strcmp(country_code, "LT") == 0) return "0200"; // Lithuania
+ if (strcmp(country_code, "LU") == 0) return "9938"; // Luxembourg
+ if (strcmp(country_code, "LV") == 0) return "9939"; // Latvia
+ if (strcmp(country_code, "MT") == 0) return "9943"; // Malta
+ if (strcmp(country_code, "NL") == 0) return "9944"; // Netherlands
+ if (strcmp(country_code, "PL") == 0) return "9945"; // Poland
+ if (strcmp(country_code, "PT") == 0) return "9946"; // Portugal
+ if (strcmp(country_code, "RO") == 0) return "9947"; // Romania
+ if (strcmp(country_code, "SE") == 0) return "0007"; // Sweden
+ if (strcmp(country_code, "SI") == 0) return "9949"; // Slovenia
+ if (strcmp(country_code, "SK") == 0) return "9950"; // Slovakia
+ if (strcmp(country_code, "ES") == 0) return "9920"; // Spain
+ return NULL; // Unknown country code
+}
+
+bool administration_writer_save_invoice_blocking(invoice inv)
+{
+ #define APPEND(_txt, ...) file_content += sprintf(file_content, _txt, __VA_ARGS__);
+
+ bool result = 1;
+ int buf_length = 15000; // Ballpark file content size.
+ char* file_content = (char*)malloc(buf_length);
+ memset(file_content, 0, buf_length);
+ memcpy(file_content, peppol_invoice_template, strlen(peppol_invoice_template));
+
+ struct tm *tm_info = 0;
+ char date_buffer[11]; // "YYYY-MM-DD" + null terminator
+
+ strops_replace(file_content, buf_length, "{{INVOICE_ID}}", inv.sequential_number);
+ strops_replace(file_content, buf_length, "{{CURRENCY}}", inv.currency);
+
+ // Supplier data
+ strops_replace(file_content, buf_length, "{{SUPPLIER_ENDPOINT_SCHEME}}", administration_writer_get_eas_scheme_for_address(inv.supplier.address));
+ strops_replace(file_content, buf_length, "{{SUPPLIER_ENDPOINT_ID}}", administration_writer_get_eas_id_for_contact(inv.supplier));
+ strops_replace(file_content, buf_length, "{{SUPPLIER_NAME}}", inv.supplier.name);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_STREET}}", inv.supplier.address.address1);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_STREET2}}", inv.supplier.address.address2);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_CITY}}", inv.supplier.address.city);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_POSTAL}}", inv.supplier.address.postal);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_REGION}}", inv.supplier.address.region);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_COUNTRY}}", inv.supplier.address.country_code);
+ strops_replace(file_content, buf_length, "{{SUPPLIER_VAT_ID}}", inv.supplier.taxid);
+
+ // Customer data
+ strops_replace(file_content, buf_length, "{{CUSTOMER_ENDPOINT_SCHEME}}", administration_writer_get_eas_scheme_for_address(inv.customer.address));
+ strops_replace(file_content, buf_length, "{{CUSTOMER_ENDPOINT_ID}}", administration_writer_get_eas_id_for_contact(inv.customer));
+ strops_replace(file_content, buf_length, "{{CUSTOMER_NAME}}", inv.customer.name);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_STREET}}", inv.customer.address.address1);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_STREET2}}", inv.customer.address.address2);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_CITY}}", inv.customer.address.city);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_POSTAL}}", inv.customer.address.postal);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_REGION}}", inv.customer.address.region);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_COUNTRY}}", inv.customer.address.country_code);
+ strops_replace(file_content, buf_length, "{{CUSTOMER_VAT_ID}}", inv.customer.taxid);
+
+ // Dates
+ tm_info = localtime(&inv.issued_at);
+ strftime(date_buffer, sizeof(date_buffer), "%Y-%m-%d", tm_info);
+ strops_replace(file_content, buf_length, "{{ISSUE_DATE}}", date_buffer);
+
+ //// Write to Disk.
+ char final_path[50];
+ snprintf(final_path, 50, "%s.xml", inv.id);
+
+ int final_length = (int)strlen(file_content);
+ if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
+ else if (!administration_writer_write_to_zip(final_path, file_content, final_length)) result = 0;
+
+ free(file_content);
+ return result;
+}
+
+static bool administration_writer_save_all_invoices_blocking()
+{
+ bool result = 1;
+ u32 num_invoices = administration_invoice_count();
+ u32 buffer_size = sizeof(invoice) * num_invoices;
+ invoice* invoice_buffer = (invoice*)malloc(buffer_size);
+ num_invoices = administration_invoice_get_all(invoice_buffer);
+
+ for (u32 i = 0; i < num_invoices; i++) {
+ invoice c = invoice_buffer[i];
+ if (!administration_writer_save_invoice_blocking(c)) result = 0;
+ }
+
+ free(invoice_buffer);
+ return result;
+}
+
+/////////////////////////////
//// Projects
/////////////////////////////
bool administration_writer_save_project_blocking(project project)
@@ -326,6 +477,7 @@ static int administration_writer_write_all_t(void *arg) {
mtx_lock(&_save_file_mutex);
+ if (!administration_writer_save_all_invoices_blocking()) result = 0;
if (!administration_writer_save_all_projects_blocking()) result = 0;
if (!administration_writer_save_all_administration_info_blocking()) result = 0;
if (!administration_writer_save_all_tax_brackets_blocking()) result = 0;