summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-10 15:25:22 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-10 15:25:22 +0200
commita88456ec309edb6778cf50c139cab6ab8e99e963 (patch)
tree10b86e0964bee9a6670b2a064fd9863290eb970b
parent0327e06b59aa20dbfec137b2287b950b5cb84960 (diff)
move id generation out of UI and into administration
-rw-r--r--docs/README.rst2
-rw-r--r--include/administration.hpp15
-rw-r--r--include/ui.hpp1
-rw-r--r--src/administration.cpp44
-rw-r--r--src/ui/ui_contacts.cpp5
-rw-r--r--src/ui/ui_invoices.cpp58
-rw-r--r--src/ui/ui_main.cpp2
-rw-r--r--src/ui/ui_projects.cpp9
8 files changed, 112 insertions, 24 deletions
diff --git a/docs/README.rst b/docs/README.rst
index 2762bf1..7dedc8d 100644
--- a/docs/README.rst
+++ b/docs/README.rst
@@ -19,7 +19,7 @@ What OpenBooks **can** do:
- OpenBooks handles administration of invoices, expenses, receipts, contacts, projects, VAT rates and cost centers.
- OpenBooks can export/email invoices in PDF, UBL and Peppol format.
- OpenBooks can send and receive invoices over the Peppol network (using a locally run `Holodeck <https://holodeck-b2b.org/>`_ instance).
-- OpenBooks can create country specific tax reports and audit files (see supported countries list).
+- OpenBooks can create tax reports and audit files (see supported countries list).
What OpenBooks **can't** do:
diff --git a/include/administration.hpp b/include/administration.hpp
index a0dd803..dffce89 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -91,7 +91,7 @@ typedef struct
typedef struct
{
char id[16];
- char sequential_number[16];
+ char sequential_number[16]; // INV0000000000 - INV9999999999
char customer_id[16];
char supplier_id[16];
time_t issued_at;
@@ -106,7 +106,7 @@ typedef struct
s32 net;
invoice_status status;
address shipping_address;
- char currency[16];
+ char currency[8];
time_t keep_untill;
time_t payment_on_account_date;
char tax_representative[64];
@@ -116,10 +116,10 @@ typedef struct
typedef struct
{
contact company_info;
- s32 next_id;
+ s32 next_id; // Shared across all objects that have an ID.
+ s32 next_sequence_number; // Sequence number for generating invoice numbers.
char path[4096];
char program_version[10];
- char country_code[3];
list_t contacts;
list_t projects;
list_t invoices;
@@ -131,7 +131,6 @@ void administration_create();
void administration_destroy();
char* administration_get_file_path();
-s32 administration_create_id();
contact administration_get_company_info();
void administration_set_company_info(contact data);
@@ -142,6 +141,7 @@ bool administration_create_contact(contact data);
bool administration_update_contact(contact data);
u32 administration_get_contact_count();
u32 administration_get_contacts(u32 page_index, u32 page_size, contact* buffer);
+contact administration_create_empty_contact();
void administration_cancel_project(project data);
bool administration_remove_project(project data);
@@ -150,6 +150,7 @@ bool administration_update_project(project data);
char* administration_project_get_status_string(project data);
u32 administration_get_project_count();
u32 administration_get_projects(u32 page_index, u32 page_size, project* buffer);
+project administration_create_empty_project();
u32 administration_get_tax_bracket_count();
u32 administration_get_tax_brackets(country_tax_bracket* buffer);
@@ -161,4 +162,6 @@ u32 administration_get_cost_centers(cost_center* buffer);
bool administration_verify_cost_center_code(char* code);
bool administration_verify_cost_center_description(char* text);
bool administration_add_cost_center(cost_center data);
-bool administration_update_cost_center(cost_center data); \ No newline at end of file
+bool administration_update_cost_center(cost_center data);
+
+invoice administration_create_empty_invoice(); \ No newline at end of file
diff --git a/include/ui.hpp b/include/ui.hpp
index 85a5c2f..00a3465 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -18,6 +18,7 @@ void ui_draw_projects();
void ui_draw_invoices();
void ui_draw_settings();
+void ui_setup_invoices();
void ui_setup_contacts();
void ui_setup_projects();
void ui_setup_settings(); \ No newline at end of file
diff --git a/src/administration.cpp b/src/administration.cpp
index c6ece04..edbc933 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -9,6 +9,11 @@
administration g_administration;
+static s32 administration_create_id()
+{
+ return g_administration.next_id;
+}
+
#define ADD_BRACKET(_country, _rate, _description)\
{\
country_tax_bracket* tb = (country_tax_bracket*)malloc(sizeof(country_tax_bracket));\
@@ -27,7 +32,6 @@ static int compare_tax_countries(const void *a, const void *b)
return strcmp(objA->country_code, objB->country_code);
}
-
static void administration_create_default_tax_brackets()
{
// General brackets shared between countries.
@@ -197,6 +201,9 @@ static void administration_create_default_cost_centers()
void administration_create()
{
+ g_administration.next_id = 1;
+ g_administration.next_sequence_number = 1;
+
list_init(&g_administration.contacts);
list_init(&g_administration.projects);
list_init(&g_administration.tax_brackets);
@@ -270,11 +277,6 @@ bool administration_remove_contact(contact data)
return false;
}
-s32 administration_create_id()
-{
- return g_administration.next_id;
-}
-
u32 administration_get_contact_count()
{
return list_size(&g_administration.contacts);
@@ -543,4 +545,34 @@ bool administration_update_cost_center(cost_center data)
list_iterator_stop(&g_administration.cost_centers);
return false;
+}
+
+static s32 administration_create_sequence_number()
+{
+ return g_administration.next_sequence_number;
+}
+
+invoice administration_create_empty_invoice()
+{
+ invoice result;
+ memset(&result, 0, sizeof(invoice));
+ snprintf(result.id, sizeof(result.id), "I/%d", administration_create_id());
+ snprintf(result.sequential_number, sizeof(result.id), "INV%010d", administration_create_sequence_number());
+ return result;
+}
+
+contact administration_create_empty_contact()
+{
+ contact result;
+ memset(&result, 0, sizeof(contact));
+ snprintf(result.id, IM_ARRAYSIZE(result.id), "C/%d", administration_create_id());
+ return result;
+}
+
+project administration_create_empty_project()
+{
+ project result;
+ memset(&result, 0, sizeof(project));
+ snprintf(result.id, IM_ARRAYSIZE(result.id), "P/%d", administration_create_id());
+ return project;
} \ No newline at end of file
diff --git a/src/ui/ui_contacts.cpp b/src/ui/ui_contacts.cpp
index 0ade9b0..e19f571 100644
--- a/src/ui/ui_contacts.cpp
+++ b/src/ui/ui_contacts.cpp
@@ -14,7 +14,7 @@ static contact active_contact;
void ui_setup_contacts()
{
current_view_state = view_state::LIST;
- memset(&active_contact, 0, sizeof(contact));
+ active_contact = active_contact = administration_create_empty_contact();
memset(&selected_for_removal, 0, sizeof(contact));
}
@@ -156,8 +156,7 @@ static void draw_contact_list()
if (ImGui::Button(localize("form.create")))
{
current_view_state = view_state::CREATE;
- memset(&active_contact, 0, sizeof(contact));
- snprintf(active_contact.id, IM_ARRAYSIZE(active_contact.id), "C/%d", administration_create_id());
+ active_contact = administration_create_empty_contact();
}
if (current_page >= max_page-1) current_page = max_page-1;
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index ea45f4e..26f60c4 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -6,10 +6,64 @@
#include "administration.hpp"
#include "locales.hpp"
-static view_state current_view_state = LIST;
+static view_state current_view_state = view_state::LIST;
static invoice active_invoice;
-void ui_draw_invoices()
+void ui_setup_invoices()
+{
+ current_view_state = view_state::LIST;
+ active_invoice = administration_create_empty_invoice();
+}
+
+bool draw_invoice_form(invoice* buffer, bool back_button_enabled = true, bool viewing_only = false)
{
+ if (back_button_enabled)
+ {
+ if (ImGui::Button(localize("form.back"))) {
+ current_view_state = view_state::LIST;
+ return false;
+ }
+ }
+ ImGui::Spacing();
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+
+ // 1. Identifier
+ //ImGui::BeginDisabled();
+ //ImGui::SetNextItemWidth(widthAvailable*0.2f);
+ //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id));
+ // 2. Sequential number
+ ImGui::SetNextItemWidth(widthAvailable*0.5f);
+ ImGui::InputTextWithHint("Invoice number", "Invoice number", buffer->sequential_number, IM_ARRAYSIZE(buffer->sequential_number));
+ ImGui::SameLine();ui_helper_draw_required_tag();
+ if (!viewing_only) ImGui::EndDisabled();
+
+ return false;
+}
+
+void draw_invoices_list()
+{
+ // Table header controls: create button and pagination.
+ if (ImGui::Button(localize("form.create")))
+ {
+ current_view_state = view_state::CREATE;
+ active_invoice = administration_create_empty_invoice();
+ }
+}
+
+void ui_draw_invoices()
+{
+ switch(current_view_state)
+ {
+ case view_state::LIST: draw_invoices_list(); break;
+ case view_state::CREATE:
+ if (draw_invoice_form(&active_invoice))
+ {
+ //administration_create_invoice(active_invoice);
+ current_view_state = view_state::LIST;
+ }
+ break;
+ case view_state::EDIT: break;
+ case view_state::VIEW: break;
+ }
} \ No newline at end of file
diff --git a/src/ui/ui_main.cpp b/src/ui/ui_main.cpp
index 10f9cea..05a98b4 100644
--- a/src/ui/ui_main.cpp
+++ b/src/ui/ui_main.cpp
@@ -28,7 +28,7 @@ void (*drawcalls[dashboard_view_state::END])(void) = {
};
void (*setupcalls[dashboard_view_state::END])(void) = {
- 0,
+ ui_setup_invoices,
0,
ui_setup_contacts,
0,
diff --git a/src/ui/ui_projects.cpp b/src/ui/ui_projects.cpp
index 377155e..5cb412f 100644
--- a/src/ui/ui_projects.cpp
+++ b/src/ui/ui_projects.cpp
@@ -13,7 +13,7 @@ static project active_project;
void ui_setup_projects()
{
current_view_state = view_state::LIST;
- memset(&active_project, 0, sizeof(contact));
+ active_project = administration_create_empty_project();
}
static void draw_project_form()
@@ -22,7 +22,7 @@ static void draw_project_form()
if (ImGui::Button(localize("form.back"))) {
current_view_state = view_state::LIST;
- memset(&active_project, 0, sizeof(project));
+ active_project = administration_create_empty_project();
selected_country = 0;
return;
}
@@ -57,9 +57,9 @@ static void draw_project_form()
else if (current_view_state == view_state::EDIT)
administration_update_project(active_project);
- memset(&active_project, 0, sizeof(project));
current_view_state = view_state::LIST;
selected_country = 0;
+ active_project = administration_create_empty_project();
}
if (!can_save) ImGui::EndDisabled();
}
@@ -78,8 +78,7 @@ static void draw_project_list()
if (ImGui::Button(localize("form.create")))
{
current_view_state = view_state::CREATE;
- memset(&active_project, 0, sizeof(project));
- snprintf(active_project.id, IM_ARRAYSIZE(active_project.id), "P/%d", administration_create_id());
+ active_project = administration_create_empty_project();
}
if (current_page >= max_page-1) current_page = max_page-1;