diff options
| author | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-03 11:03:50 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-03 11:03:50 +0100 |
| commit | 83a9739b3aff75cf767db687bd531fa5283e0e72 (patch) | |
| tree | adf130cb7728a4358062a040f6322ecc236d0e6b | |
| parent | 2218ef68056ebc5a3a416e2dd7e8e020fba60a4f (diff) | |
move invoice status to invoice extras struct. implement r/w
| -rw-r--r-- | include/administration.hpp | 10 | ||||
| -rw-r--r-- | include/administration_reader.hpp | 4 | ||||
| -rw-r--r-- | include/file_templates.hpp | 13 | ||||
| -rw-r--r-- | src/administration.cpp | 3 | ||||
| -rw-r--r-- | src/administration_reader.cpp | 40 | ||||
| -rw-r--r-- | src/administration_writer.cpp | 38 | ||||
| -rw-r--r-- | src/importer.cpp | 2 | ||||
| -rw-r--r-- | src/ui/ui_expenses.cpp | 4 | ||||
| -rw-r--r-- | src/ui/ui_invoices.cpp | 7 | ||||
| -rw-r--r-- | tests/administration_rw_tests.cpp | 4 | ||||
| -rw-r--r-- | tests/peppol_write_tests.cpp | 2 | ||||
| -rw-r--r-- | tests/test_helper.cpp | 2 |
12 files changed, 100 insertions, 29 deletions
diff --git a/include/administration.hpp b/include/administration.hpp index fede22a..d899987 100644 --- a/include/administration.hpp +++ b/include/administration.hpp @@ -280,6 +280,11 @@ typedef struct typedef struct { + invoice_status status; +} invoice_extras; + +typedef struct +{ char id[MAX_LEN_ID]; // I/[id] char sequential_number[MAX_LEN_SEQ_NUM]; // INV0000000000 - INV9999999999 time_t issued_at; @@ -298,13 +303,14 @@ typedef struct char currency[MAX_LEN_CURRENCY]; // 3 letter code bool is_triangulation; // True if addressee != customer - invoice_status status; bool is_outgoing; // Outgoing or incomming invoice. payment_information payment_means; - + contact supplier; contact customer; delivery_info addressee; + + invoice_extras extras; // Stored outside of invoice file. } invoice; typedef struct diff --git a/include/administration_reader.hpp b/include/administration_reader.hpp index b2311af..bb9ba29 100644 --- a/include/administration_reader.hpp +++ b/include/administration_reader.hpp @@ -16,6 +16,8 @@ #pragma once +#include <zip.h> + namespace administration_reader { bool open_new(); @@ -26,7 +28,7 @@ namespace administration_reader { bool import_cost_center(char* buffer, size_t buffer_size); bool import_project(char* buffer, size_t buffer_size); bool import_contact(char* buffer, size_t buffer_size); - bool import_invoice(char* buffer, size_t buffer_size); + bool import_invoice(zip_t* zip, char* buffer, size_t buffer_size); bool read_invoice_from_xml(invoice* result, char* buffer, size_t buffer_size); }
\ No newline at end of file diff --git a/include/file_templates.hpp b/include/file_templates.hpp index c5e0191..5404270 100644 --- a/include/file_templates.hpp +++ b/include/file_templates.hpp @@ -126,6 +126,11 @@ namespace file_template { " </cac:Price>\n" " </cac:InvoiceLine>\n"; + static const char *invoice_extras_template = + "<InvoiceExtras>\n" + " <Status>{{INVOICE_STATUS}}</Status>\n" + "</InvoiceExtras>\n"; + static const char *peppol_invoice_template = /*"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"*/ "<Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\"\n" @@ -141,10 +146,10 @@ namespace file_template { " <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>\n" " <cbc:DocumentCurrencyCode>{{CURRENCY}}</cbc:DocumentCurrencyCode>\n" "\n" - " <cac:DespatchDocumentReference>\n" - " <cbc:ID>{{INVOICE_STATUS}}</cbc:ID>\n" - " </cac:DespatchDocumentReference>\n" - "\n" + // " <cac:DespatchDocumentReference>\n" + // " <cbc:ID>{{INVOICE_STATUS}}</cbc:ID>\n" + // " </cac:DespatchDocumentReference>\n" + // "\n" " <cac:AdditionalDocumentReference>\n" " <cbc:ID>{{INVOICE_DOCUMENT_COPY}}</cbc:ID>\n" " <cbc:DocumentDescription>{{INVOICE_DOCUMENT_ORIG}}</cbc:DocumentDescription>\n" diff --git a/src/administration.cpp b/src/administration.cpp index 77e8754..6e7d2f1 100644 --- a/src/administration.cpp +++ b/src/administration.cpp @@ -1397,6 +1397,7 @@ invoice administration::invoice_create_empty() strops::format(result.sequential_number, sizeof(result.id), "INV%010d", create_sequence_number()); result.issued_at = time(NULL); result.issued_at -= (result.issued_at % 86400); + result.extras.status = invoice_status::INVOICE_CONCEPT; result.delivered_at = result.issued_at; result.expires_at = result.issued_at + administration::get_default_invoice_expire_duration(); @@ -1752,7 +1753,7 @@ u32 administration::invoice_get_partial_list_incomming(u32 page_index, u32 page_ char* administration::invoice_get_status_string(invoice* invoice) { - switch(invoice->status) + switch(invoice->extras.status) { case invoice_status::INVOICE_CONCEPT: return "invoice.status.concept"; case invoice_status::INVOICE_SENT: return "invoice.status.sent"; diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp index d790b91..3706e72 100644 --- a/src/administration_reader.cpp +++ b/src/administration_reader.cpp @@ -92,7 +92,7 @@ bool administration_reader::open_existing(char* file_path) } else if (strops::is_prefixed("I/", name)) { - administration_reader::import_invoice(buffer, (size_t)size); + administration_reader::import_invoice(zip, buffer, (size_t)size); } memops::unalloc(buffer); } @@ -123,7 +123,7 @@ bool administration_reader::read_invoice_from_xml(invoice* result, char* buffer, xml_get_str(root, data.id, MAX_LEN_ID, "cbc:ID"); xml_get_str_x(root, data.sequential_number, MAX_LEN_ID, "cac:OrderReference", "cbc:ID", 0); xml_get_str(root, data.currency, MAX_LEN_CURRENCY, "cbc:DocumentCurrencyCode"); - data.status = (invoice_status)xml_get_s32_x(root, "cac:DespatchDocumentReference", "cbc:ID", 0); + //data.status = (invoice_status)xml_get_s32_x(root, "cac:DespatchDocumentReference", "cbc:ID", 0); // Dates data.issued_at = xml_get_date_x(root, "cbc:IssueDate", 0); @@ -232,12 +232,46 @@ bool administration_reader::read_invoice_from_xml(invoice* result, char* buffer, return true; } -bool administration_reader::import_invoice(char* buffer, size_t buffer_size) +static bool _import_invoice_extras(zip_t* zip, invoice* data) +{ + STOPWATCH_START; + + char final_path[50]; + strops::format(final_path, 50, "EXTRAS/%s.xml", data->sequential_number); + if (zip_entry_open(zip, final_path) < 0) { + logger::error("ERROR loading invoice extras for '%s', file '%s' does not exist.", data->sequential_number, final_path); + return false; + } + + // Load into buffer. + char* buffer; + unsigned long long size; + zip_entry_read(zip, (void**)&buffer, (size_t*)&size); + + xml_document* document = xml_parse_document((uint8_t *)buffer, size); + if (!document) return false; + + struct xml_node* root = xml_document_root(document); + if (!root) { + xml_document_free(document, false); + return false; + } + + data->extras.status = (invoice_status)xml_get_s32_x(root, "Status", 0); + + logger::info("Loaded invoice extras '%s' in %.3fms.", final_path, STOPWATCH_TIME); + + return true; +} + +bool administration_reader::import_invoice(zip_t* zip, char* buffer, size_t buffer_size) { STOPWATCH_START; invoice data; if (!administration_reader::read_invoice_from_xml(&data, buffer, buffer_size)) return false; + if (!_import_invoice_extras(zip, &data)) return false; + printf("%d\n", data.extras.status); a_err result = administration::invoice_import(&data); if (result == A_ERR_SUCCESS) { diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp index 8304e5c..3191be7 100644 --- a/src/administration_writer.cpp +++ b/src/administration_writer.cpp @@ -240,7 +240,7 @@ void administration_writer::destroy() static char* copy_template(const char* template_str, int* buf_size) { size_t template_size = strops::length(template_str); - size_t buf_length = template_size*5; // Ballpark file content size. + size_t buf_length = template_size*5; // Ballpark file content size. // @TODO char* file_content = (char*)memops::alloc(buf_length); memops::zero(file_content, buf_length); memops::copy(file_content, template_str, template_size); @@ -504,12 +504,40 @@ static void _add_document_to_zip(invoice* inv) } } +bool _save_invoice_extras(invoice inv) +{ + STOPWATCH_START; + + bool result = 1; + int buf_length = 0; + char* file_content = copy_template(file_template::invoice_extras_template, &buf_length); + + strops::replace_int32(file_content, buf_length, "{{INVOICE_STATUS}}", (s32)inv.extras.status); + + //// Write to Disk. + char final_path[50]; + strops::format(final_path, 50, "EXTRAS/%s.xml", inv.sequential_number); + + int final_length = (int)strops::length(file_content); + if (!xml_string_is_valid((uint8_t*)file_content, final_length)) result = 0; + else if (!write_to_zip(final_path, file_content, final_length)) result = 0; + + memops::unalloc(file_content); + + if (result) logger::info("Saved invoice extras for '%s' in %.3fms.", inv.sequential_number, STOPWATCH_TIME); + else logger::error("Failed to save invoice extras for '%s'.", inv.sequential_number); + + return result; +} + bool administration_writer::save_invoice_blocking(invoice inv) { + _save_invoice_extras(inv); + STOPWATCH_START; bool result = 1; - int buf_length = 150000; // Ballpark file content size. + int buf_length = 150000; // Ballpark file content size. // @TODO char* file_content = (char*)memops::alloc(buf_length); memops::zero(file_content, buf_length); memops::copy(file_content, file_template::peppol_invoice_template, strops::length(file_template::peppol_invoice_template)); @@ -526,7 +554,7 @@ bool administration_writer::save_invoice_blocking(invoice inv) strops::replace(file_content, buf_length, "{{COST_CENTER_ID}}", inv.cost_center_id); strops::replace(file_content, buf_length, "{{INVOICE_DOCUMENT_COPY}}", inv.document.copy_path); strops::replace(file_content, buf_length, "{{INVOICE_DOCUMENT_ORIG}}", inv.document.original_path); - strops::replace_int32(file_content, buf_length, "{{INVOICE_STATUS}}", (s32)inv.status); + //strops::replace_int32(file_content, buf_length, "{{INVOICE_STATUS}}", (s32)inv.status); // Supplier data strops::replace(file_content, buf_length, "{{SUPPLIER_ENDPOINT_SCHEME}}", get_eas_scheme_for_contact(inv.supplier)); @@ -587,7 +615,7 @@ bool administration_writer::save_invoice_blocking(invoice inv) tax_rate* tax_rate_buffer = (tax_rate*)memops::alloc(sizeof(tax_rate)*administration::billing_item_count(&inv)); u32 tax_rate_count = administration::invoice_get_tax_rates(&inv, tax_rate_buffer); - u32 tax_subtotal_list_buffer_size = (u32)strops::length(file_template::peppol_invoice_tax_subtotal_template) * 2 * tax_rate_count; // Ballpark list size. + u32 tax_subtotal_list_buffer_size = (u32)strops::length(file_template::peppol_invoice_tax_subtotal_template) * 2 * tax_rate_count; // Ballpark list size. // @TODO char* tax_subtotal_list_buffer = (char*)memops::alloc(tax_subtotal_list_buffer_size); memops::zero(tax_subtotal_list_buffer, tax_subtotal_list_buffer_size); u32 tax_subtotal_list_buffer_cursor = 0; @@ -631,7 +659,7 @@ bool administration_writer::save_invoice_blocking(invoice inv) billing_item* billing_item_buffer = (billing_item*)memops::alloc(sizeof(billing_item) * administration::billing_item_count(&inv)); u32 billing_item_count = administration::billing_item_get_all_for_invoice(&inv, billing_item_buffer); - u32 billing_item_list_buffer_size = (u32)strops::length(file_template::peppol_invoice_line_template) * 2 * billing_item_count; // Ballpark list size. + u32 billing_item_list_buffer_size = (u32)strops::length(file_template::peppol_invoice_line_template) * 2 * billing_item_count; // Ballpark list size. // @TODO char* billing_item_list_buffer = (char*)memops::alloc(billing_item_list_buffer_size); memops::zero(billing_item_list_buffer, billing_item_list_buffer_size); u32 billing_item_list_buffer_cursor = 0; diff --git a/src/importer.cpp b/src/importer.cpp index 3c56062..65726bc 100644 --- a/src/importer.cpp +++ b/src/importer.cpp @@ -255,7 +255,7 @@ static int _ai_document_to_invoice_t(void *arg) return 0; } - inv.status = invoice_status::INVOICE_RECEIVED; + inv.extras.status = invoice_status::INVOICE_RECEIVED; inv.is_triangulation = !memops::equals(&inv.addressee.address, &inv.customer.address, sizeof(address)); diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp index 4227e08..7f8d1cc 100644 --- a/src/ui/ui_expenses.cpp +++ b/src/ui/ui_expenses.cpp @@ -179,7 +179,7 @@ static void draw_expenses_list() active_invoice = administration::invoice_create_empty(); active_invoice.customer = administration::company_info_get(); active_invoice.is_outgoing = 0; - active_invoice.status = invoice_status::INVOICE_RECEIVED; + active_invoice.extras.status = invoice_status::INVOICE_RECEIVED; } char import_file_path[MAX_LEN_PATH] = {0}; @@ -189,7 +189,7 @@ static void draw_expenses_list() active_invoice = administration::invoice_create_empty(); active_invoice.customer = administration::company_info_get(); active_invoice.is_outgoing = 0; - active_invoice.status = invoice_status::INVOICE_RECEIVED; + active_invoice.extras.status = invoice_status::INVOICE_RECEIVED; active_import_request = importer::ai_document_to_invoice(import_file_path); } diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp index a5fbd3c..519544b 100644 --- a/src/ui/ui_invoices.cpp +++ b/src/ui/ui_invoices.cpp @@ -287,7 +287,6 @@ static void draw_invoices_list() _set_active_invoice(administration::invoice_create_empty()); active_invoice.supplier = administration::company_info_get(); active_invoice.is_outgoing = 1; - active_invoice.status = invoice_status::INVOICE_CONCEPT; } if (current_page >= max_page-1) current_page = max_page-1; @@ -478,7 +477,11 @@ static void draw_send_options() active_request = exporter::send_email("test@test-vz9dlemj2564kj50.mlsender.net", "aldrikboy@gmail.com", "test", "test 123", [](e_err status) { if (status == E_ERR_SUCCESS) { - administration::activity_add(ACTIVITY_USER, active_invoice.id, "Sent email", 0); + active_invoice.extras.status = invoice_status::INVOICE_SENT; + administration::invoice_update(&active_invoice); + administration::activity_add(ACTIVITY_USER, active_invoice.id, "Invoice status changed", 0); // @locale + administration::activity_add(ACTIVITY_USER, active_invoice.id, "Sent email", 0); // @locale + _reload_activities(); } }); diff --git a/tests/administration_rw_tests.cpp b/tests/administration_rw_tests.cpp index be52f5f..a33173f 100644 --- a/tests/administration_rw_tests.cpp +++ b/tests/administration_rw_tests.cpp @@ -241,7 +241,6 @@ TEST _administration_rw_b2b_invoice(void) inv.supplier = pw1; inv.customer = pw2; inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; @@ -285,7 +284,6 @@ TEST _administration_rw_b2c_invoice(void) inv.supplier = pw1; inv.customer = pw2; inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; @@ -329,7 +327,6 @@ TEST _administration_rw_b2b2c_invoice(void) inv.addressee.address = _create_nl_consumer().address; inv.is_triangulation = 1; inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; @@ -373,7 +370,6 @@ TEST _administration_rw_b2c_2currency_invoice(void) inv.supplier = pw1; inv.customer = pw2; inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; diff --git a/tests/peppol_write_tests.cpp b/tests/peppol_write_tests.cpp index 35cf940..a967477 100644 --- a/tests/peppol_write_tests.cpp +++ b/tests/peppol_write_tests.cpp @@ -12,7 +12,6 @@ TEST _peppol_write_nl2nl_b2b(void) inv.supplier = _create_nl_business(); inv.customer = _create_nl_business(); inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; @@ -40,7 +39,6 @@ TEST _peppol_write_nl2nl_b2c(void) inv.supplier = _create_nl_business(); inv.customer = _create_nl_consumer(); inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; diff --git a/tests/test_helper.cpp b/tests/test_helper.cpp index c99bdd0..7b1bf96 100644 --- a/tests/test_helper.cpp +++ b/tests/test_helper.cpp @@ -152,7 +152,6 @@ static invoice _create_nl_b2b_inv_outgoing() inv.supplier = administration::company_info_get(); inv.customer = _create_nl_business(); inv.is_outgoing = 1; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; @@ -165,7 +164,6 @@ static invoice _create_nl_b2b_inv_incomming() inv.supplier = _create_nl_business(); inv.customer = administration::company_info_get(); inv.is_outgoing = 0; - inv.status = invoice_status::INVOICE_CONCEPT; inv.issued_at = time(NULL); inv.delivered_at = inv.issued_at; inv.expires_at = inv.issued_at + 1000; |
