summaryrefslogtreecommitdiff
path: root/src/administration.cpp
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-16 11:10:29 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-16 11:10:29 +0200
commit05bc81cd42c5aeff7cfb6cf6b18f88792e7c16c9 (patch)
tree7bc9c0439507b4531ed78578e441b12cba4dda4e /src/administration.cpp
parentf67e92f55b6223f2806c3d5ef1cbe2a638920562 (diff)
invoice form work
Diffstat (limited to 'src/administration.cpp')
-rw-r--r--src/administration.cpp195
1 files changed, 188 insertions, 7 deletions
diff --git a/src/administration.cpp b/src/administration.cpp
index 56d8663..2e16775 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -262,6 +262,12 @@ static void administration_create_debug_data()
ADD_PROJECT("Retail store #1");
ADD_PROJECT("Retail store #2");
ADD_PROJECT("Kayak rental");
+
+ // Company info.
+ strops_copy(g_administration.company_info.name, "Aldrik Ramaekers", sizeof(g_administration.company_info.name));
+ strops_copy(g_administration.company_info.address.address1, "Keerderstraat 81", sizeof(g_administration.company_info.address.address1));
+ strops_copy(g_administration.company_info.address.address2, "6226XW Maastricht", sizeof(g_administration.company_info.address.address2));
+ strops_copy(g_administration.company_info.address.country_code, "NL", sizeof(g_administration.company_info.address.country_code));
}
void administration_create()
@@ -269,11 +275,6 @@ void administration_create()
g_administration.next_id = 1;
g_administration.next_sequence_number = 1;
- strops_copy(g_administration.company_info.name, "Aldrik Ramaekers", sizeof(g_administration.company_info.name));
- strops_copy(g_administration.company_info.address.address1, "Keerderstraat 81", sizeof(g_administration.company_info.address.address1));
- strops_copy(g_administration.company_info.address.address2, "6226XW Maastricht", sizeof(g_administration.company_info.address.address2));
- strops_copy(g_administration.company_info.address.country_code, "NL", sizeof(g_administration.company_info.address.country_code));
-
list_init(&g_administration.contacts);
list_init(&g_administration.projects);
list_init(&g_administration.tax_brackets);
@@ -408,6 +409,25 @@ u32 administration_get_project_count()
return list_size(&g_administration.projects);
}
+u32 administration_get_billing_items_count(invoice* invoice)
+{
+ return list_size(&invoice->billing_items);
+}
+
+u32 administration_get_all_billing_items_for_invoice(invoice* invoice, billing_item* buffer)
+{
+ u32 write_cursor = 0;
+
+ list_iterator_start(&invoice->billing_items);
+ while (list_iterator_hasnext(&invoice->billing_items)) {
+ billing_item c = *(billing_item *)list_iterator_next(&invoice->billing_items);
+ buffer[write_cursor++] = c;
+ }
+ list_iterator_stop(&invoice->billing_items);
+
+ return write_cursor;
+}
+
u32 administration_get_all_projects(project* buffer)
{
u32 write_cursor = 0;
@@ -543,6 +563,23 @@ bool administration_add_tax_bracket(country_tax_bracket data)
return true;
}
+u32 administration_get_tax_brackets_for_country(country_tax_bracket* buffer, char* country_code)
+{
+ assert(buffer);
+
+ u32 write_cursor = 0;
+
+ list_iterator_start(&g_administration.tax_brackets);
+ while (list_iterator_hasnext(&g_administration.tax_brackets)) {
+ country_tax_bracket c = *(country_tax_bracket *)list_iterator_next(&g_administration.tax_brackets);
+
+ if (strcmp(c.country_code, country_code) == 0 || strcmp(c.country_code, "00") == 0) buffer[write_cursor++] = c;
+ }
+ list_iterator_stop(&g_administration.tax_brackets);
+
+ return write_cursor;
+}
+
u32 administration_get_tax_brackets(country_tax_bracket* buffer)
{
assert(buffer);
@@ -576,8 +613,6 @@ bool administration_update_tax_bracket(country_tax_bracket data)
return false;
}
-
-
u32 administration_get_cost_center_count()
{
return list_size(&g_administration.cost_centers);
@@ -689,6 +724,68 @@ static time_t administration_get_default_invoice_expire_duration()
return (30 * 24 * 60 * 60); // 30 days
}
+billing_item administration_create_empty_billing_item()
+{
+ billing_item item;
+ memset(&item, 0, sizeof(billing_item));
+ item.amount = 1;
+ return item;
+}
+
+bool administration_add_billing_item_to_invoice(invoice* invoice, billing_item item)
+{
+ billing_item* tb = (billing_item*)malloc(sizeof(billing_item));
+ memcpy(tb, &item, sizeof(billing_item));
+ snprintf(tb->id, sizeof(tb->id), "B/%d", administration_create_id());
+ strops_copy(tb->invoice_id, invoice->id, sizeof(tb->invoice_id));
+ list_append(&invoice->billing_items, tb);
+ strops_copy(tb->currency, invoice->currency, CURRENCY_LENGTH); // Set billing item currency to invoice currency.
+
+ g_administration.next_id++;
+
+ return true;
+}
+
+static char* administration_get_default_currency_for_country(char* country_code)
+{
+ if (country_code == NULL || strlen(country_code) != 2)
+ return "EUR"; // default
+
+ // Non-euro EU currencies
+ if (strcmp(country_code, "BG") == 0) return "BGN"; // Bulgaria
+ else if (strcmp(country_code, "CZ") == 0) return "CZK"; // Czechia
+ else if (strcmp(country_code, "DK") == 0) return "DKK"; // Denmark
+ else if (strcmp(country_code, "HU") == 0) return "HUF"; // Hungary
+ else if (strcmp(country_code, "PL") == 0) return "PLN"; // Poland
+ else if (strcmp(country_code, "RO") == 0) return "RON"; // Romania
+ else if (strcmp(country_code, "SE") == 0) return "SEK"; // Sweden
+
+ // Eurozone members
+ else if (strcmp(country_code, "AT") == 0) return "EUR"; // Austria
+ else if (strcmp(country_code, "BE") == 0) return "EUR"; // Belgium
+ else if (strcmp(country_code, "CY") == 0) return "EUR"; // Cyprus
+ else if (strcmp(country_code, "DE") == 0) return "EUR"; // Germany
+ else if (strcmp(country_code, "EE") == 0) return "EUR"; // Estonia
+ else if (strcmp(country_code, "ES") == 0) return "EUR"; // Spain
+ else if (strcmp(country_code, "FI") == 0) return "EUR"; // Finland
+ else if (strcmp(country_code, "FR") == 0) return "EUR"; // France
+ else if (strcmp(country_code, "GR") == 0) return "EUR"; // Greece
+ else if (strcmp(country_code, "HR") == 0) return "EUR"; // Croatia
+ else if (strcmp(country_code, "IE") == 0) return "EUR"; // Ireland
+ else if (strcmp(country_code, "IT") == 0) return "EUR"; // Italy
+ else if (strcmp(country_code, "LT") == 0) return "EUR"; // Lithuania
+ else if (strcmp(country_code, "LU") == 0) return "EUR"; // Luxembourg
+ else if (strcmp(country_code, "LV") == 0) return "EUR"; // Latvia
+ else if (strcmp(country_code, "MT") == 0) return "EUR"; // Malta
+ else if (strcmp(country_code, "NL") == 0) return "EUR"; // Netherlands
+ else if (strcmp(country_code, "PT") == 0) return "EUR"; // Portugal
+ else if (strcmp(country_code, "SI") == 0) return "EUR"; // Slovenia
+ else if (strcmp(country_code, "SK") == 0) return "EUR"; // Slovakia
+
+ // Default fallback
+ return "EUR";
+}
+
invoice administration_create_empty_invoice()
{
invoice result;
@@ -698,6 +795,8 @@ invoice administration_create_empty_invoice()
result.issued_at = time(NULL);
result.delivered_at = time(NULL);
result.expires_at = time(NULL) + 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), CURRENCY_LENGTH);
return result;
}
@@ -715,4 +814,86 @@ project administration_create_empty_project()
memset(&result, 0, sizeof(project));
snprintf(result.id, sizeof(result.id), "P/%d", administration_create_id());
return result;
+}
+
+static bool administration_get_tax_bracket_by_id(country_tax_bracket* buffer, char* id)
+{
+ assert(buffer);
+
+ list_iterator_start(&g_administration.tax_brackets);
+ while (list_iterator_hasnext(&g_administration.tax_brackets)) {
+ country_tax_bracket c = *(country_tax_bracket *)list_iterator_next(&g_administration.tax_brackets);
+ if (strcmp(c.id, id) == 0)
+ {
+ *buffer = c;
+ list_iterator_stop(&g_administration.tax_brackets);
+ return true;
+ }
+ }
+ list_iterator_stop(&g_administration.tax_brackets);
+
+ return false;
+}
+
+static void administration_recalculate_billing_item_totals(billing_item* item)
+{
+ if (item->amount_is_percentage)
+ {
+ item->net = item->net_per_item * (item->amount / 100.0f);
+ }
+ else
+ {
+ item->net = item->net_per_item * item->amount;
+ }
+
+ if (item->discount != 0)
+ {
+ if (item->discount_is_percentage)
+ {
+ item->net -= item->net * (item->discount / 100.0f);
+ }
+ else
+ {
+ item->net -= item->discount;
+ }
+ }
+
+ country_tax_bracket bracket;
+ if (administration_get_tax_bracket_by_id(&bracket, item->tax_bracket_id))
+ {
+ item->tax = item->net * (bracket.rate/100.0f);
+ }
+
+ item->total = item->net + item->tax;
+}
+
+bool administration_update_billing_item_of_invoice(invoice* invoice, billing_item item)
+{
+ list_iterator_start(&invoice->billing_items);
+ while (list_iterator_hasnext(&invoice->billing_items)) {
+ billing_item* c = (billing_item *)list_iterator_next(&invoice->billing_items);
+
+ if (strcmp(c->id, item.id) == 0) {
+ memcpy(c, &item, sizeof(billing_item));
+ administration_recalculate_billing_item_totals(c);
+ list_iterator_stop(&invoice->billing_items);
+ return true;
+ }
+ }
+ list_iterator_stop(&invoice->billing_items);
+
+ return false;
+}
+
+void administration_invoice_set_currency(invoice* invoice, char* currency)
+{
+ strops_copy(invoice->currency, currency, CURRENCY_LENGTH);
+
+ list_iterator_start(&invoice->billing_items);
+ while (list_iterator_hasnext(&invoice->billing_items)) {
+ billing_item* c = (billing_item *)list_iterator_next(&invoice->billing_items);
+
+ strops_copy(c->currency, currency, CURRENCY_LENGTH);
+ }
+ list_iterator_stop(&invoice->billing_items);
} \ No newline at end of file