summaryrefslogtreecommitdiff
path: root/src/administration.cpp
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-17 17:35:06 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-17 17:35:06 +0200
commit8ea59863c5d13e68e080cf7612047ea4c655292c (patch)
treec9e71b722854cb10e34de193e77f5e3220d9d4e1 /src/administration.cpp
parentb676299f983b6fc00458953d90efe3f784b86d62 (diff)
debug data for invoices and expenses
Diffstat (limited to 'src/administration.cpp')
-rw-r--r--src/administration.cpp259
1 files changed, 194 insertions, 65 deletions
diff --git a/src/administration.cpp b/src/administration.cpp
index 45a1125..68714d3 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -32,6 +32,76 @@ static int compare_tax_countries(const void *a, const void *b)
return strcmp(objA->country_code, objB->country_code);
}
+static invoice_status administation_get_random_invoice_status()
+{
+ return (invoice_status)(rand() % invoice_status::INVOICE_END);
+}
+
+static void administation_get_random_project(char* project_id)
+{
+ int size = list_size(&g_administration.projects);
+ if (size > 0) {
+ int index = rand() % size;
+ project *val = (project *) list_get_at(&g_administration.projects, index);
+ strops_copy(project_id, val->id, MAX_LEN_ID);
+ }
+}
+
+static void administation_get_random_cost_center(char* cost_center_id)
+{
+ int size = list_size(&g_administration.cost_centers);
+ if (size > 0) {
+ int index = rand() % size;
+ cost_center *val = (cost_center *) list_get_at(&g_administration.cost_centers, index);
+ strops_copy(cost_center_id, val->id, MAX_LEN_ID);
+ }
+}
+
+static contact* administation_get_random_contact()
+{
+ int size = list_size(&g_administration.contacts);
+ if (size > 0) {
+ int index = rand() % size;
+ contact *val = (contact *) list_get_at(&g_administration.contacts, index);
+ return val;
+ }
+ return 0;
+}
+
+static time_t administration_get_default_invoice_expire_duration()
+{
+ return (30 * 24 * 60 * 60); // 30 days
+}
+
+static void administration_recalculate_billing_item_totals(billing_item* item);
+
+static void administation_get_random_billing_items(invoice* inv)
+{
+ int amount = 1 + rand() % 20;
+
+ for (int i = 0; i < amount; i++)
+ {
+ billing_item item = administration_billing_item_create_empty();
+ item.amount = 1 + (float)(rand() % 5);
+ item.amount_is_percentage = (float)(rand() % 2);
+ item.description[MAX_LEN_LONG_DESC];
+ item.net_per_item = (float)(rand() % 100);
+ item.discount = (float)(rand() % 30);
+ item.discount_is_percentage = (float)(rand() % 2);
+
+ administration_recalculate_billing_item_totals(&item);
+
+ if (item.net < item.discount) item.discount = 0.0f;
+
+ country_tax_bracket buffer[20];
+ u32 bracket_count = administration_tax_bracket_get_by_country(buffer, inv->supplier.address.country_code);
+ country_tax_bracket rand_bracket = buffer[rand() % bracket_count];
+ strops_copy(item.tax_bracket_id, rand_bracket.id, MAX_LEN_ID);
+
+ administration_billing_item_add_to_invoice(inv, item);
+ }
+}
+
static void administration_create_default_tax_brackets()
{
// General brackets shared between countries.
@@ -201,6 +271,8 @@ static void administration_create_default_cost_centers()
static void administration_create_debug_data()
{
+ srand((unsigned) time(NULL));
+
#define ADD_CONSUMER(_name, _addr1, _addr2, _cc)\
{contact _c = administration_contact_create_empty();\
strops_copy(_c.name, _name, sizeof(_c.name));\
@@ -264,7 +336,6 @@ static void administration_create_debug_data()
ADD_PROJECT("Kayak rental");
// Company info.
-
snprintf(g_administration.company_info.id, sizeof(g_administration.company_info.id), "C/%d", administration_create_id());
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));
@@ -273,6 +344,27 @@ static void administration_create_debug_data()
strops_copy(g_administration.company_info.taxid, "123", sizeof(g_administration.company_info.taxid));
strops_copy(g_administration.company_info.businessid, "123", sizeof(g_administration.company_info.businessid));
g_administration.next_id++;
+
+ // Invoices
+ #define ADD_INVOICE(_outgoing)\
+ {\
+ invoice inv = administration_invoice_create_empty();\
+ inv.supplier = administration_company_info_get();\
+ inv.customer = *administation_get_random_contact();\
+ administation_get_random_project(inv.project_id);\
+ administation_get_random_cost_center(inv.cost_center_id);\
+ inv.is_outgoing = _outgoing;\
+ inv.status = administation_get_random_invoice_status();\
+ inv.issued_at = time(NULL) - (86400 * (rand() % 720));\
+ inv.delivered_at = inv.issued_at;\
+ inv.expires_at = inv.issued_at + administration_get_default_invoice_expire_duration();\
+ administation_get_random_billing_items(&inv);\
+ administration_invoice_add(&inv);\
+ }
+
+ // Create about 2 years of random data.
+ for (int i = 0; i < 410; i++) ADD_INVOICE(1);
+ for (int i = 0; i < 700; i++) ADD_INVOICE(0);
}
static s32 administration_create_sequence_number()
@@ -280,11 +372,6 @@ static s32 administration_create_sequence_number()
return g_administration.next_sequence_number;
}
-static time_t administration_get_default_invoice_expire_duration()
-{
- return (30 * 24 * 60 * 60); // 30 days
-}
-
// Setup functions.
// =======================
void administration_create()
@@ -301,6 +388,7 @@ void administration_create()
administration_create_default_tax_brackets();
administration_create_default_cost_centers();
+
administration_create_debug_data();
}
@@ -429,6 +517,14 @@ u32 administration_contact_get_partial_list(u32 page_index, u32 page_size, conta
bool administration_contact_get_by_id(contact* buffer, char* id)
{
+ // Include company info in contact lookup because this might be
+ // used in forms.
+ if (strcmp(id, g_administration.company_info.id) == 0)
+ {
+ *buffer = g_administration.company_info;
+ return true;
+ }
+
bool result = false;
list_iterator_start(&g_administration.contacts);
while (list_iterator_hasnext(&g_administration.contacts)) {
@@ -900,6 +996,10 @@ bool administration_invoice_remove(invoice* inv)
list_iterator_stop(&g_administration.invoices);
administration_destroy_list(&c->billing_items);
list_delete(&g_administration.invoices, c);
+
+ if (inv->is_outgoing) g_administration.invoice_count--;
+ else g_administration.expense_count--;
+
free(c);
return true;
}
@@ -908,21 +1008,27 @@ bool administration_invoice_remove(invoice* inv)
return false;
}
-bool administration_invoice_update(invoice* inv)
+static void administration_invoice_set_refs(invoice* inv)
{
- if (!administration_invoice_is_valid(inv)) return false;
+ // This function makes sure that contact info referenced in the IDs actually
+ // matches the contact info stored in administation, if not we make new contacts.
- // Invoice is valid but customer id is unset means we need to register a new contact.
- if (strcmp(inv->customer_id, "") == 0)
+ // Check if supplier info is equal to contact stored in supplier id, in case we autocomplete and edit data after,
+ // this should be handled as a new contact.
+ contact lookup_buffer;
+ if (administration_contact_get_by_id(&lookup_buffer, inv->supplier.id))
{
- contact new_contact = administration_contact_create_empty();
- strops_copy(inv->customer_id, new_contact.id, sizeof(new_contact.id));
- strops_copy(inv->customer.id, new_contact.id, sizeof(new_contact.id));
-
- memcpy(&new_contact, &inv->customer, sizeof(contact));
- administration_contact_add(new_contact);
-
- inv->customer = new_contact;
+ if (!administration_contact_equals(lookup_buffer, inv->supplier))
+ {
+ // Zero id so new contact is created for supplier.
+ inv->supplier_id[0] = '\0';
+ inv->supplier.id[0] = '\0';
+ }
+ else
+ {
+ // Store supplier id in invoice, (only id is stored to disk, supplier field is filled on load).
+ strops_copy(inv->supplier_id, inv->supplier.id, sizeof(inv->supplier_id));
+ }
}
// Supplier is valid but supplier id is unset means we need to register a new contact.
@@ -938,11 +1044,48 @@ bool administration_invoice_update(invoice* inv)
inv->supplier = new_contact;
}
+ // Check if customer info is equal to contact stored in customer id, in case we autocomplete and edit data after,
+ // this should be handled as a new contact.
+ if (administration_contact_get_by_id(&lookup_buffer, inv->customer.id))
+ {
+ if (!administration_contact_equals(lookup_buffer, inv->customer))
+ {
+ // Zero id so new contact is created for supplier.
+ inv->customer_id[0] = '\0';
+ inv->customer.id[0] = '\0';
+ }
+ else
+ {
+ // Store customer id in invoice, (only id is stored to disk, supplier field is filled on load).
+ strops_copy(inv->customer_id, inv->customer.id, sizeof(inv->customer_id));
+ }
+ }
+
+ // Invoice is valid but customer id is unset means we need to register a new contact.
+ if (strcmp(inv->customer_id, "") == 0)
+ {
+ contact new_contact = administration_contact_create_empty();
+ strops_copy(inv->customer_id, new_contact.id, sizeof(new_contact.id));
+ strops_copy(inv->customer.id, new_contact.id, sizeof(new_contact.id));
+
+ memcpy(&new_contact, &inv->customer, sizeof(contact));
+ administration_contact_add(new_contact);
+
+ inv->customer = new_contact;
+ }
+
// Addressee is same as customer.
if (!inv->is_triangulation)
{
memcpy(&inv->addressee, &inv->customer, sizeof(contact));
}
+}
+
+bool administration_invoice_update(invoice* inv)
+{
+ if (!administration_invoice_is_valid(inv)) return false;
+
+ administration_invoice_set_refs(inv);
list_iterator_start(&g_administration.invoices);
while (list_iterator_hasnext(&g_administration.invoices)) {
@@ -959,6 +1102,27 @@ bool administration_invoice_update(invoice* inv)
return false;
}
+bool administration_invoice_add(invoice* inv)
+{
+ if (!administration_invoice_is_valid(inv)) return false;
+
+ administration_invoice_set_refs(inv);
+
+ invoice copy = administration_invoice_create_copy(inv); // Create copy to make copy of billing item list.
+ invoice* new_inv = (invoice*)malloc(sizeof(invoice));
+ memcpy(new_inv, &copy, sizeof(invoice));
+
+ list_append(&g_administration.invoices, new_inv);
+
+ g_administration.next_id++;
+ g_administration.next_sequence_number++;
+
+ if (inv->is_outgoing) g_administration.invoice_count++;
+ else g_administration.expense_count++;
+
+ return true;
+}
+
invoice administration_invoice_create_copy(invoice* inv)
{
invoice new_inv = administration_invoice_create_empty();
@@ -980,57 +1144,19 @@ invoice administration_invoice_create_copy(invoice* inv)
return new_inv;
}
-bool administration_invoice_add(invoice* inv)
+u32 administration_invoice_count()
{
- if (!administration_invoice_is_valid(inv)) return false;
-
- // Invoice is valid but customer id is unset means we need to register a new contact.
- if (strcmp(inv->customer_id, "") == 0)
- {
- contact new_contact = administration_contact_create_empty();
- strops_copy(inv->customer_id, new_contact.id, sizeof(new_contact.id));
- strops_copy(inv->customer.id, new_contact.id, sizeof(new_contact.id));
-
- memcpy(&new_contact, &inv->customer, sizeof(contact));
- administration_contact_add(new_contact);
-
- inv->customer = new_contact;
- }
-
- // Supplier is valid but supplier id is unset means we need to register a new contact.
- if (strcmp(inv->supplier_id, "") == 0)
- {
- contact new_contact = administration_contact_create_empty();
- strops_copy(inv->supplier_id, new_contact.id, sizeof(new_contact.id));
- strops_copy(inv->supplier.id, new_contact.id, sizeof(new_contact.id));
-
- memcpy(&new_contact, &inv->supplier, sizeof(contact));
- administration_contact_add(new_contact);
-
- inv->supplier = new_contact;
- }
-
- // Addressee is same as customer.
- if (!inv->is_triangulation)
- {
- memcpy(&inv->addressee, &inv->customer, sizeof(contact));
- }
-
- invoice copy = administration_invoice_create_copy(inv); // Create copy to make copy of billing item list.
- invoice* new_inv = (invoice*)malloc(sizeof(invoice));
- memcpy(new_inv, &copy, sizeof(invoice));
-
- list_append(&g_administration.invoices, new_inv);
-
- g_administration.next_id++;
- g_administration.next_sequence_number++;
+ return list_size(&g_administration.invoices);
+}
- return true;
+u32 administation_invoice_get_incomming_count()
+{
+ return g_administration.expense_count;
}
-u32 administration_invoice_count()
+u32 administation_invoice_get_outgoing_count()
{
- return list_size(&g_administration.invoices);
+ return g_administration.invoice_count;
}
static u32 administration_invoice_get_partial_list(u32 page_index, u32 page_size, invoice* buffer, bool want_outgoing)
@@ -1184,9 +1310,12 @@ bool administration_billing_item_add_to_invoice(invoice* invoice, billing_item i
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, MAX_LEN_CURRENCY); // Set billing item currency to invoice currency.
+
+ administration_recalculate_billing_item_totals(tb);
+ administration_recalculate_invoice_totals(invoice);
+ list_append(&invoice->billing_items, tb);
g_administration.next_id++;
return true;