From 38019a9693375ac6719ffec43bff63774e142387 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sat, 13 Sep 2025 18:47:01 +0200 Subject: invoice peppol work --- src/administration_writer.cpp | 65 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) (limited to 'src/administration_writer.cpp') diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp index f8b4302..779a5f1 100644 --- a/src/administration_writer.cpp +++ b/src/administration_writer.cpp @@ -229,7 +229,7 @@ bool administration_writer_save_invoice_blocking(invoice inv) STOPWATCH_START; bool result = 1; - int buf_length = 150000; // Ballpark file content size. + int buf_length = 150000; // 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)); @@ -237,12 +237,31 @@ bool administration_writer_save_invoice_blocking(invoice inv) struct tm *tm_info = 0; char date_buffer[11]; // "YYYY-MM-DD" + null terminator + // properties not stored from invoice: + // - id (can be retrieved from filename) + // - invoice allowance (can be recalculated from billing items) + + // properties not stored from supplier/customer/addressee: + // - type + // - bank account + // These can all be retrieved from existing contacts. + + // properties not stored from billing item: + // - invoice_id (not necessary) + // - discount (can be recalculated from line_amount - (quantity * unit_price) ) + // - tax (can be recalculated) + // - total (can be recalculated) + strops_replace(file_content, buf_length, "{{INVOICE_ID}}", inv.sequential_number); strops_replace(file_content, buf_length, "{{CURRENCY}}", inv.currency); + strops_replace(file_content, buf_length, "{{PROJECT_ID}}", inv.project_id); + strops_replace(file_content, buf_length, "{{COST_CENTER_CODE}}", inv.cost_center_id); + strops_replace(file_content, buf_length, "{{INVOICE_DOCUMENT}}", inv.document); // 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_ID}}", inv.supplier.id); 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); @@ -251,10 +270,15 @@ bool administration_writer_save_invoice_blocking(invoice inv) 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); + strops_replace(file_content, buf_length, "{{SUPPLIER_LEGAL_NAME}}", inv.supplier.name); + strops_replace(file_content, buf_length, "{{SUPPLIER_BUSINESS_ID}}", inv.supplier.businessid); + strops_replace(file_content, buf_length, "{{SUPPLIER_PHONE_NUMBER}}", inv.supplier.phone_number); + strops_replace(file_content, buf_length, "{{SUPPLIER_EMAIL}}", inv.supplier.email); // 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_ID}}", inv.customer.id); 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); @@ -263,6 +287,22 @@ bool administration_writer_save_invoice_blocking(invoice inv) 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); + strops_replace(file_content, buf_length, "{{CUSTOMER_LEGAL_NAME}}", inv.customer.name); + strops_replace(file_content, buf_length, "{{CUSTOMER_BUSINESS_ID}}", inv.customer.businessid); + strops_replace(file_content, buf_length, "{{CUSTOMER_PHONE_NUMBER}}", inv.customer.phone_number); + strops_replace(file_content, buf_length, "{{CUSTOMER_EMAIL}}", inv.customer.email); + + // Delivery data + tm_info = localtime(&inv.delivered_at); + strftime(date_buffer, sizeof(date_buffer), "%Y-%m-%d", tm_info); + strops_replace(file_content, buf_length, "{{DELIVERY_DATE}}", date_buffer); + strops_replace(file_content, buf_length, "{{DELIVERY_NAME}}", inv.addressee.name); + strops_replace(file_content, buf_length, "{{DELIVERY_STREET}}", inv.addressee.address.address1); + strops_replace(file_content, buf_length, "{{DELIVERY_STREET2}}", inv.addressee.address.address2); + strops_replace(file_content, buf_length, "{{DELIVERY_CITY}}", inv.addressee.address.city); + strops_replace(file_content, buf_length, "{{DELIVERY_POSTAL}}", inv.addressee.address.postal); + strops_replace(file_content, buf_length, "{{DELIVERY_REGION}}", inv.addressee.address.region); + strops_replace(file_content, buf_length, "{{DELIVERY_COUNTRY}}", inv.addressee.address.country_code); // Payment means strops_replace_int32(file_content, buf_length, "{{PAYMENT_TYPE}}", inv.payment_means.payment_method); @@ -270,7 +310,7 @@ bool administration_writer_save_invoice_blocking(invoice inv) strops_replace(file_content, buf_length, "{{SUPPLIER_BIC}}", inv.payment_means.service_provider_id); // Tax breakdown - strops_replace_float(file_content, buf_length, "{{TOTAL_TAX_AMOUNT}}", inv.total, 2); + strops_replace_float(file_content, buf_length, "{{TOTAL_TAX_AMOUNT}}", inv.tax, 2); { // Create tax subtotal list. country_tax_bracket* tax_bracket_buffer = (country_tax_bracket*)malloc(sizeof(country_tax_bracket)*administration_billing_item_count(&inv)); u32 tax_bracket_count = administration_invoice_get_tax_brackets(&inv, tax_bracket_buffer); @@ -337,10 +377,23 @@ bool administration_writer_save_invoice_blocking(invoice inv) strops_replace(billing_item_file_content, billing_item_buf_length, "{{ITEM_NAME}}", bi.description); strops_replace(billing_item_file_content, billing_item_buf_length, "{{LINE_TAX_CATEGORY}}", bracket.category_code); strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{LINE_TAX_PERCENT}}", bracket.rate, 2); - strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{LINE_AMOUNT}}", bi.net, 2); + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{LINE_AMOUNT}}", bi.net, 2); // line amount = net_per_item * items_count - discount strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{QUANTITY}}", bi.amount, 2); - strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{UNIT_PRICE}}", bi.net_per_item, 2); + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{UNIT_PRICE}}", bi.net_per_item, 2); // unit price before discount strops_replace(billing_item_file_content, billing_item_buf_length, "{{UNIT_CODE}}", bi.amount_is_percentage ? "%" : "X"); + strops_replace(billing_item_file_content, billing_item_buf_length, "{{TAX_BRACKET_ID}}", bi.tax_bracket_id); + + if (bi.discount_is_percentage) { + strops_replace(billing_item_file_content, billing_item_buf_length, "{{ALLOWANCE_IS_PERCENTAGE}}", + "{{DISCOUNT_TOTAL_PERCENTAGE}}"); + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_TOTAL_PERCENTAGE}}", bi.allowance / (bi.net + bi.allowance), 2); + } + else { + strops_replace(billing_item_file_content, billing_item_buf_length, "{{ALLOWANCE_IS_PERCENTAGE}}", ""); + } + + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_TOTAL}}", bi.allowance, 2); + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_BASE_AMOUNT}}", bi.net + bi.allowance, 2); // Total net before discount. u32 content_len = (u32)strlen(billing_item_file_content); memcpy(billing_item_list_buffer+billing_item_list_buffer_cursor, billing_item_file_content, content_len); @@ -360,6 +413,10 @@ bool administration_writer_save_invoice_blocking(invoice inv) strftime(date_buffer, sizeof(date_buffer), "%Y-%m-%d", tm_info); strops_replace(file_content, buf_length, "{{ISSUE_DATE}}", date_buffer); + tm_info = localtime(&inv.expires_at); + strftime(date_buffer, sizeof(date_buffer), "%Y-%m-%d", tm_info); + strops_replace(file_content, buf_length, "{{DUE_DATE}}", date_buffer); + //// Write to Disk. char final_path[50]; snprintf(final_path, 50, "%s.xml", inv.id); -- cgit v1.2.3-70-g09d2