diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/administration.cpp | 92 | ||||
| -rw-r--r-- | src/administration_reader.cpp | 246 | ||||
| -rw-r--r-- | src/administration_writer.cpp | 25 |
3 files changed, 340 insertions, 23 deletions
diff --git a/src/administration.cpp b/src/administration.cpp index 690fcd9..a21c70f 100644 --- a/src/administration.cpp +++ b/src/administration.cpp @@ -1012,7 +1012,7 @@ a_err administration_contact_get_by_id(contact* buffer, char* id) contact c = *(contact *)list_iterator_next(&g_administration.contacts); if (strcmp(c.id, id) == 0) { - list_iterator_stop(&g_administration.projects); + list_iterator_stop(&g_administration.contacts); *buffer = c; result = A_ERR_SUCCESS; } @@ -1579,8 +1579,10 @@ invoice administration_invoice_create_empty() snprintf(result.id, sizeof(result.id), "I/%d", administration_create_id()); snprintf(result.sequential_number, sizeof(result.id), "INV%010d", administration_create_sequence_number()); result.issued_at = time(NULL); - result.delivered_at = time(NULL); - result.expires_at = time(NULL) + administration_get_default_invoice_expire_duration(); + result.issued_at -= (result.issued_at % 86400); + + result.delivered_at = result.issued_at; + result.expires_at = result.issued_at + administration_get_default_invoice_expire_duration(); list_init(&result.billing_items); // @leak strops_copy(result.currency, administration_get_default_currency_for_country(g_administration.company_info.address.country_code), MAX_LEN_CURRENCY); return result; @@ -1767,12 +1769,20 @@ a_err administration_invoice_update(invoice* inv) return A_ERR_NOT_FOUND; } -a_err administration_invoice_add(invoice* inv) +a_err administration_invoice_import(invoice* inv) { a_err result = administration_invoice_is_valid(inv); if (result != A_ERR_SUCCESS) return result; - administration_invoice_set_refs(inv); + if (!inv->is_triangulation) + { + memcpy(&inv->addressee, &inv->customer, sizeof(contact)); + } + + inv->issued_at -= (inv->issued_at % 86400); + inv->delivered_at -= (inv->delivered_at % 86400); + inv->expires_at -= (inv->expires_at % 86400); + inv->is_outgoing = strcmp(inv->supplier.id, MY_COMPANY_ID) == 0; invoice copy = administration_invoice_create_copy(inv); // Create copy to make copy of billing item list. invoice* new_inv = (invoice*)malloc(sizeof(invoice)); @@ -1780,11 +1790,36 @@ a_err administration_invoice_add(invoice* inv) memcpy(new_inv, ©, sizeof(invoice)); - new_inv->payment_means.payment_method = PAYMENT_METHOD_STANDING_AGREEMENT; - strops_copy(new_inv->payment_means.payee_bank_account, inv->customer.bank_account, sizeof(new_inv->payment_means.payee_bank_account)); - strops_copy(new_inv->payment_means.payee_account_name, inv->customer.name, sizeof(new_inv->payment_means.payee_account_name)); - strops_copy(new_inv->payment_means.service_provider_id, "", sizeof(new_inv->payment_means.service_provider_id)); - strops_copy(new_inv->payment_means.payer_bank_account, inv->supplier.bank_account, sizeof(new_inv->payment_means.payer_bank_account)); + if (!list_append(&g_administration.invoices, new_inv)) { + return A_ERR_GENERIC; + } + + return A_ERR_SUCCESS; +} + +a_err administration_invoice_add(invoice* inv) +{ + a_err result = administration_invoice_is_valid(inv); + if (result != A_ERR_SUCCESS) return result; + + administration_invoice_set_refs(inv); + + inv->issued_at -= (inv->issued_at % 86400); + inv->delivered_at -= (inv->delivered_at % 86400); + inv->expires_at -= (inv->expires_at % 86400); + inv->is_outgoing = strcmp(inv->supplier.id, MY_COMPANY_ID) == 0; + + inv->payment_means.payment_method = PAYMENT_METHOD_STANDING_AGREEMENT; + strops_copy(inv->payment_means.payee_bank_account, inv->customer.bank_account, sizeof(inv->payment_means.payee_bank_account)); + strops_copy(inv->payment_means.payee_account_name, inv->customer.name, sizeof(inv->payment_means.payee_account_name)); + strops_copy(inv->payment_means.service_provider_id, "", sizeof(inv->payment_means.service_provider_id)); + strops_copy(inv->payment_means.payer_bank_account, inv->supplier.bank_account, sizeof(inv->payment_means.payer_bank_account)); + + invoice copy = administration_invoice_create_copy(inv); // Create copy to make copy of billing item list. + invoice* new_inv = (invoice*)malloc(sizeof(invoice)); + if (!new_inv) return A_ERR_GENERIC; + + memcpy(new_inv, ©, sizeof(invoice)); if (!list_append(&g_administration.invoices, new_inv)) { return A_ERR_GENERIC; @@ -1838,6 +1873,24 @@ u32 administration_invoice_get_outgoing_count() return g_administration.invoice_count; } +a_err administration_invoice_get_by_id(invoice* buffer, char* id) +{ + a_err result = A_ERR_NOT_FOUND; + list_iterator_start(&g_administration.invoices); + while (list_iterator_hasnext(&g_administration.invoices)) { + invoice c = *(invoice *)list_iterator_next(&g_administration.invoices); + + if (strcmp(c.id, id) == 0) { + list_iterator_stop(&g_administration.invoices); + *buffer = c; + result = A_ERR_SUCCESS; + } + } + list_iterator_stop(&g_administration.invoices); + + return result; +} + u32 administration_invoice_get_all(invoice* buffer) { assert(buffer); @@ -2065,6 +2118,25 @@ a_err administration_billing_item_is_valid(billing_item item) return result; } +a_err administration_billing_item_import_to_invoice(invoice* invoice, billing_item item) +{ + if (administration_billing_item_count(invoice) >= MAX_BILLING_ITEMS) return A_ERR_MAX_ITEMS_REACHED; + + billing_item* tb = (billing_item*)malloc(sizeof(billing_item)); + if (!tb) return A_ERR_GENERIC; + + memcpy(tb, &item, sizeof(billing_item)); + + administration_recalculate_billing_item_totals(tb); + administration_recalculate_invoice_totals(invoice); + + if (!list_append(&invoice->billing_items, tb)) { + return A_ERR_GENERIC; + } + + return A_ERR_SUCCESS; +} + a_err administration_billing_item_add_to_invoice(invoice* invoice, billing_item item) { if (administration_billing_item_count(invoice) >= MAX_BILLING_ITEMS) return A_ERR_MAX_ITEMS_REACHED; diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp index 8c6bf89..3ea6316 100644 --- a/src/administration_reader.cpp +++ b/src/administration_reader.cpp @@ -14,11 +14,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <malloc.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <zip.h> #include <xml.h> +#include <time.h> #include "log.hpp" #include "strops.hpp" @@ -104,6 +106,10 @@ bool administration_reader_open_existing(char* file_path) { administration_reader_import_contact(buffer, (size_t)size); } + else if (strops_prefix("I/", name)) + { + administration_reader_import_invoice(buffer, (size_t)size); + } free(buffer); } @@ -136,6 +142,64 @@ static s64 _get_xml_s64(xml_node* root, char* child_name) static s32 _get_xml_s32(xml_node* root, char* child_name) { struct xml_node* node = xml_easy_child(root, (uint8_t *)child_name, 0); + if (!node) return 0; + + char xml_content[512]; + memset(xml_content, 0, 512); + struct xml_string* str = xml_node_content(node); + xml_string_copy(str, (uint8_t *)xml_content, xml_string_length(str)); + + char *endptr; + long val = strtol(xml_content, &endptr, 10); + + s32 num = (int32_t) val; + return num; +} + +static xml_node* _get_xml_node_x(xml_node* root, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + return node; +} + +static char* _get_xml_str_attribute(xml_node* root, char* buffer, size_t bufsize, char* attribute_name, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + if (!node) return 0; + + size_t num_attributes = xml_node_attributes(node); + for (int x = 0; x < num_attributes; x++) + { + struct xml_string* attr_name = xml_node_attribute_name(node, x); + size_t b_length = xml_string_length(attr_name); + uint8_t* b_buffer = (uint8_t*)alloca((b_length + 1) * sizeof(uint8_t)); + xml_string_copy(attr_name, b_buffer, b_length); + b_buffer[b_length] = 0; + + if (strcmp((char*)b_buffer, attribute_name) == 0) { + struct xml_string* attr_content = xml_node_attribute_content(node, x); + + xml_string_copy(attr_content, (uint8_t *)buffer, bufsize); + buffer[bufsize-1] = 0; + return buffer; + } + } + return 0; +} + +static s32 _get_xml_s32_x(xml_node* root, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + if (!node) return 0; char xml_content[512]; memset(xml_content, 0, 512); @@ -152,6 +216,25 @@ static s32 _get_xml_s32(xml_node* root, char* child_name) static float _get_xml_float(xml_node* root, char* child_name) { struct xml_node* node = xml_easy_child(root, (uint8_t *)child_name, 0); + if (!node) return 0; + + char xml_content[512]; + memset(xml_content, 0, 512); + struct xml_string* str = xml_node_content(node); + xml_string_copy(str, (uint8_t *)xml_content, xml_string_length(str)); + + char *endptr; + float val = strtof(xml_content, &endptr); + return val; +} + +static float _get_xml_float_x(xml_node* root, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + if (!node) return 0; char xml_content[512]; memset(xml_content, 0, 512); @@ -166,6 +249,7 @@ static float _get_xml_float(xml_node* root, char* child_name) static char* _get_xml_str(xml_node* root, char* buffer, size_t bufsize, char* child_name) { struct xml_node* node = xml_easy_child(root, (uint8_t *)child_name, 0); + if (!node) return 0; memset(buffer, 0, bufsize); struct xml_string* str = xml_node_content(node); @@ -174,6 +258,168 @@ static char* _get_xml_str(xml_node* root, char* buffer, size_t bufsize, char* ch return buffer; } +static char* _get_xml_str_x(xml_node* root, char* buffer, size_t bufsize, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + if (!node) return 0; + + memset(buffer, 0, bufsize); + struct xml_string* str = xml_node_content(node); + xml_string_copy(str, (uint8_t *)buffer, xml_string_length(str)); + + return buffer; +} + +static time_t _get_xml_date_x(xml_node* root, char* child_name, ...) +{ + va_list arguments; + va_start(arguments, child_name); + + struct xml_node* node = xml_easy_vchild(root, (const uint8_t *)child_name, arguments); + if (!node) return 0; + + char date_buffer[11]; + struct xml_string* str = xml_node_content(node); + xml_string_copy(str, (uint8_t *)date_buffer, xml_string_length(str)); + + struct tm tm_info = {0}; + int year, month, day; + if (sscanf(date_buffer, "%d-%d-%d", &year, &month, &day) != 3) { + return (time_t)-1; // parse failed + } + + tm_info.tm_year = year - 1900; // struct tm expects years since 1900 + tm_info.tm_mon = month - 1; // struct tm months are 0–11 + tm_info.tm_mday = day; + tm_info.tm_hour = 0; + tm_info.tm_min = 0; + tm_info.tm_sec = 0; + + return mktime(&tm_info) + 86400; // Hack +} + +bool administration_reader_import_invoice(char* buffer, size_t buffer_size) +{ + STOPWATCH_START; + + xml_document* document = xml_parse_document((uint8_t *)buffer, buffer_size); + if (!document) return false; + + struct xml_node* root = xml_document_root(document); + + invoice data = administration_invoice_create_empty(); + _get_xml_str(root, data.id, MAX_LEN_ID, "cbc:ID"); + _get_xml_str_x(root, data.sequential_number, MAX_LEN_ID, "cac:OrderReference", "cbc:ID", 0); + _get_xml_str(root, data.currency, MAX_LEN_CURRENCY, "cbc:DocumentCurrencyCode"); + data.status = (invoice_status)_get_xml_s32_x(root, "cac:DespatchDocumentReference", "cbc:ID", 0); + + // Dates + data.issued_at = _get_xml_date_x(root, "cbc:IssueDate", 0); + data.expires_at = _get_xml_date_x(root, "cbc:DueDate", 0); + data.delivered_at = _get_xml_date_x(root, "cac:Delivery", "cbc:ActualDeliveryDate", 0); + + // References + _get_xml_str_x(root, data.document, MAX_LEN_PATH, "cac:AdditionalDocumentReference", "cbc:ID", 0); + _get_xml_str_x(root, data.project_id, MAX_LEN_ID, "cac:ProjectReference", "cbc:ID", 0); + _get_xml_str(root, data.cost_center_id, MAX_LEN_ID, "cac:AccountingCost"); + + // Payment means + data.payment_means.payment_method = (payment_method)_get_xml_s32_x(root, "cac:PaymentMeans", "cbc:PaymentMeansCode", 0); + _get_xml_str_x(root, data.payment_means.payee_bank_account, MAX_LEN_BANK, "cac:PaymentMeans", "cac:PayeeFinancialAccount", "cbc:ID", 0); + _get_xml_str_x(root, data.payment_means.payee_account_name, MAX_LEN_LONG_DESC, "cac:PaymentMeans", "cac:PayeeFinancialAccount", "cbc:Name", 0); + _get_xml_str_x(root, data.payment_means.service_provider_id, MAX_LEN_ID, "cac:PaymentMeans", "cac:PayeeFinancialAccount", "cac:FinancialInstitutionBranch", "cac:FinancialInstitution", "cbc:ID", 0); + _get_xml_str_x(root, data.payment_means.payer_bank_account, MAX_LEN_BANK, "cac:PaymentMeans", "cac:PayerFinancialAccount", "cbc:ID", 0); + + // Totals + data.tax = _get_xml_float_x(root, "cac:TaxTotal", "cbc:TaxAmount", 0); + data.total = _get_xml_float_x(root, "cac:LegalMonetaryTotal", "cbc:TaxInclusiveAmount", 0); + data.net = _get_xml_float_x(root, "cac:LegalMonetaryTotal", "cbc:TaxExclusiveAmount", 0); + data.allowance = data.net - _get_xml_float_x(root, "cac:LegalMonetaryTotal", "cbc:LineExtensionAmount", 0); + + // Supplier + _get_xml_str_x(root, data.supplier.id, MAX_LEN_ID, "cac:AccountingSupplierParty", "cac:Party", "cac:Contact", "cbc:Name", 0); + strops_copy(data.supplier_id, data.supplier.id, MAX_LEN_ID); + strops_copy(data.supplier.bank_account, data.payment_means.payee_bank_account, MAX_LEN_BANK); + _get_xml_str_x(root, data.supplier.name, MAX_LEN_LONG_DESC, "cac:AccountingSupplierParty", "cac:Party", "cac:PartyName", "cbc:Name", 0); + _get_xml_str_x(root, data.supplier.address.address1, MAX_LEN_ADDRESS, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cbc:StreetName", 0); + _get_xml_str_x(root, data.supplier.address.address2, MAX_LEN_ADDRESS, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cbc:AdditionalStreetName", 0); + _get_xml_str_x(root, data.supplier.address.city, MAX_LEN_ADDRESS, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cbc:CityName", 0); + _get_xml_str_x(root, data.supplier.address.postal, MAX_LEN_ADDRESS, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cbc:PostalZone", 0); + _get_xml_str_x(root, data.supplier.address.region, MAX_LEN_ADDRESS, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cbc:CountrySubentity", 0); + _get_xml_str_x(root, data.supplier.address.country_code, MAX_LEN_COUNTRY_CODE, "cac:AccountingSupplierParty", "cac:Party", "cac:PostalAddress", "cac:Country", "cbc:IdentificationCode", 0); + _get_xml_str_x(root, data.supplier.taxid, MAX_LEN_TAXID, "cac:AccountingSupplierParty", "cac:Party", "cac:PartyTaxScheme", "cbc:CompanyID", 0); + _get_xml_str_x(root, data.supplier.businessid, MAX_LEN_BUSINESSID, "cac:AccountingSupplierParty", "cac:Party", "cac:PartyIdentification", "cbc:ID", 0); + _get_xml_str_x(root, data.supplier.phone_number, MAX_LEN_PHONE, "cac:AccountingSupplierParty", "cac:Party", "cac:Contact", "cbc:Telephone", 0); + _get_xml_str_x(root, data.supplier.email, MAX_LEN_EMAIL, "cac:AccountingSupplierParty", "cac:Party", "cac:Contact", "cbc:ElectronicMail", 0); + + // Customer + _get_xml_str_x(root, data.customer.id, MAX_LEN_ID, "cac:AccountingCustomerParty", "cac:Party", "cac:Contact", "cbc:Name", 0); + strops_copy(data.customer_id, data.customer.id, MAX_LEN_ID); + strops_copy(data.customer.bank_account, data.payment_means.payer_bank_account, MAX_LEN_BANK); + _get_xml_str_x(root, data.customer.name, MAX_LEN_LONG_DESC, "cac:AccountingCustomerParty", "cac:Party", "cac:PartyName", "cbc:Name", 0); + _get_xml_str_x(root, data.customer.address.address1, MAX_LEN_ADDRESS, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cbc:StreetName", 0); + _get_xml_str_x(root, data.customer.address.address2, MAX_LEN_ADDRESS, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cbc:AdditionalStreetName", 0); + _get_xml_str_x(root, data.customer.address.city, MAX_LEN_ADDRESS, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cbc:CityName", 0); + _get_xml_str_x(root, data.customer.address.postal, MAX_LEN_ADDRESS, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cbc:PostalZone", 0); + _get_xml_str_x(root, data.customer.address.region, MAX_LEN_ADDRESS, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cbc:CountrySubentity", 0); + _get_xml_str_x(root, data.customer.address.country_code, MAX_LEN_COUNTRY_CODE, "cac:AccountingCustomerParty", "cac:Party", "cac:PostalAddress", "cac:Country", "cbc:IdentificationCode", 0); + _get_xml_str_x(root, data.customer.taxid, MAX_LEN_TAXID, "cac:AccountingCustomerParty", "cac:Party", "cac:PartyTaxScheme", "cbc:CompanyID", 0); + _get_xml_str_x(root, data.customer.businessid, MAX_LEN_BUSINESSID, "cac:AccountingCustomerParty", "cac:Party", "cac:PartyIdentification", "cbc:ID", 0); + _get_xml_str_x(root, data.customer.phone_number, MAX_LEN_PHONE, "cac:AccountingCustomerParty", "cac:Party", "cac:Contact", "cbc:Telephone", 0); + _get_xml_str_x(root, data.customer.email, MAX_LEN_EMAIL, "cac:AccountingCustomerParty", "cac:Party", "cac:Contact", "cbc:ElectronicMail", 0); + + // Addressee + _get_xml_str_x(root, data.addressee.name, MAX_LEN_LONG_DESC, "cac:Delivery", "cac:DeliveryParty", "cac:PartyName", "cbc:Name", 0); + _get_xml_str_x(root, data.addressee.address.address1, MAX_LEN_ADDRESS, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cbc:StreetName", 0); + _get_xml_str_x(root, data.addressee.address.address2, MAX_LEN_ADDRESS, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cbc:AdditionalStreetName", 0); + _get_xml_str_x(root, data.addressee.address.city, MAX_LEN_ADDRESS, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cbc:CityName", 0); + _get_xml_str_x(root, data.addressee.address.postal, MAX_LEN_ADDRESS, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cbc:PostalZone", 0); + _get_xml_str_x(root, data.addressee.address.region, MAX_LEN_ADDRESS, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cbc:CountrySubentity", 0); + _get_xml_str_x(root, data.addressee.address.country_code, MAX_LEN_COUNTRY_CODE, "cac:Delivery", "cac:DeliveryLocation", "cac:Address", "cac:Country", "cbc:IdentificationCode", 0); + + size_t child_count = xml_node_children(root); + for (size_t x = 0; x < child_count; x++) + { + xml_node* child = xml_node_child(root, x); + + char* child_name = (char*)xml_easy_name(child); + if (strcmp(child_name, "cac:InvoiceLine") == 0) + { + + billing_item bi = {0}; + _get_xml_str_x(child, bi.id, MAX_LEN_ID, "cbc:ID", 0); + _get_xml_str_x(child, bi.tax_rate_id, MAX_LEN_ID, "cac:Item", "cac:AdditionalItemProperty", "cbc:Value", 0); + bi.amount = _get_xml_float_x(child, "cbc:InvoicedQuantity", 0); + bi.net_per_item = _get_xml_float_x(child, "cac:Price", "cbc:PriceAmount", 0); + bi.net = _get_xml_float_x(child, "cbc:LineExtensionAmount", 0); + bi.discount = _get_xml_float_x(child, "cac:AllowanceCharge", "cbc:Amount", 0); + _get_xml_str_x(child, bi.description, MAX_LEN_LONG_DESC, "cac:Item", "cbc:Name", 0); + + char percentage_buffer[5] = {0}; + _get_xml_str_attribute(child, percentage_buffer, 5, "unitCode", "cbc:InvoicedQuantity", 0); + bi.amount_is_percentage = strcmp(percentage_buffer, "%") == 0; + + _get_xml_str_attribute(child, bi.currency, 5, "currencyID", "cbc:LineExtensionAmount", 0); + bi.discount_is_percentage = _get_xml_node_x(child, "cac:AllowanceCharge", "cbc:MultiplierFactorNumeric", 0) != 0; + if (bi.discount_is_percentage) { + bi.discount = _get_xml_float_x(child, "cac:AllowanceCharge", "cbc:MultiplierFactorNumeric", 0); + } + + administration_billing_item_import_to_invoice(&data, bi); + } + + free(child_name); + } + + bool result = administration_invoice_import(&data); + log_add("Loaded invoice '%s' in %.3fms.", data.sequential_number, STOPWATCH_TIME); + + return result; +} + bool administration_reader_import_contact(char* buffer, size_t buffer_size) { STOPWATCH_START; diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp index e02d476..75f5daf 100644 --- a/src/administration_writer.cpp +++ b/src/administration_writer.cpp @@ -298,13 +298,8 @@ 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: @@ -312,11 +307,13 @@ bool administration_writer_save_invoice_blocking(invoice inv) // - 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, "{{INVOICE_ID}}", inv.id); + strops_replace(file_content, buf_length, "{{INVOICE_SEQUENCE_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, "{{COST_CENTER_ID}}", inv.cost_center_id); strops_replace(file_content, buf_length, "{{INVOICE_DOCUMENT}}", inv.document); + strops_replace_int32(file_content, buf_length, "{{INVOICE_STATUS}}", (s32)inv.status); // Supplier data strops_replace(file_content, buf_length, "{{SUPPLIER_ENDPOINT_SCHEME}}", administration_writer_get_eas_scheme_for_contact(inv.supplier)); @@ -366,9 +363,11 @@ bool administration_writer_save_invoice_blocking(invoice inv) // Payment means strops_replace_int32(file_content, buf_length, "{{PAYMENT_TYPE}}", inv.payment_means.payment_method); - strops_replace(file_content, buf_length, "{{SUPPLIER_IBAN}}", inv.payment_means.payee_bank_account); - strops_replace(file_content, buf_length, "{{SUPPLIER_BIC}}", inv.payment_means.service_provider_id); - + strops_replace(file_content, buf_length, "{{RECIPIENT_IBAN}}", inv.payment_means.payee_bank_account); + strops_replace(file_content, buf_length, "{{RECIPIENT_NAME}}", inv.payment_means.payee_account_name); + strops_replace(file_content, buf_length, "{{RECIPIENT_BIC}}", inv.payment_means.service_provider_id); + strops_replace(file_content, buf_length, "{{SENDER_IBAN}}", inv.payment_means.payer_bank_account); + // Tax breakdown strops_replace_float(file_content, buf_length, "{{TOTAL_TAX_AMOUNT}}", inv.tax, 2); { // Create tax subtotal list. @@ -431,9 +430,10 @@ bool administration_writer_save_invoice_blocking(invoice inv) billing_item bi = billing_item_buffer[i]; tax_rate rate; administration_tax_rate_get_by_id(&rate, bi.tax_rate_id); - + strops_replace(billing_item_file_content, billing_item_buf_length, "{{CURRENCY}}", bi.currency); strops_replace(billing_item_file_content, billing_item_buf_length, "{{LINE_ID}}", bi.id); + strops_replace(billing_item_file_content, billing_item_buf_length, "{{LINE_TAX_ID}}", bi.tax_rate_id); 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}}", rate.category_code); strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{LINE_TAX_PERCENT}}", rate.rate, 2); @@ -441,10 +441,9 @@ bool administration_writer_save_invoice_blocking(invoice inv) 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); // 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_rate_id); if (bi.discount_is_percentage) { - strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_TOTAL_PERCENTAGE}}", (bi.allowance / (bi.net + bi.allowance)) * 100.0f, 2); + strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_TOTAL_PERCENTAGE}}", bi.discount, 2); strops_replace_float(billing_item_file_content, billing_item_buf_length, "{{DISCOUNT_BASE_AMOUNT}}", bi.net + bi.allowance, 2); // Total net before discount. } else { |
