summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-10-12 13:14:11 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-10-12 13:14:11 +0200
commitb6990df3d783da0a09e8b6a825d9ebeb7314466a (patch)
treec9a858fcacd3c24b05a6a5d9da00e6a28e16dcc3
parent955af14f6664574df1ff47e073e39f8d48d6355a (diff)
mark invalid invoices, projects, contacts in ui
-rw-r--r--TODO4
-rw-r--r--include/ui.hpp1
-rw-r--r--manual/01_features.md4
-rw-r--r--src/administration.cpp12
-rw-r--r--src/locales/en.cpp3
-rw-r--r--src/ui/imgui_extensions.cpp36
-rw-r--r--src/ui/ui_contacts.cpp8
-rw-r--r--src/ui/ui_expenses.cpp8
-rw-r--r--src/ui/ui_invoices.cpp10
-rw-r--r--src/ui/ui_projects.cpp8
10 files changed, 76 insertions, 18 deletions
diff --git a/TODO b/TODO
index c88972a..b205b9e 100644
--- a/TODO
+++ b/TODO
@@ -2,7 +2,7 @@ TODO:
- for invoice importing using AI: all address data should be editable because import is not perfect
- for invoice importing using AI: file path should not be editable as it is imported
- create invoice PDF for NL https://goedestartbelastingdienst.nl/wiki/view/50bdccd8-f9a0-4297-b57f-3a6651cbe05c/factuureisen
-- toggle on invoice form wether price in inclusive of tax.
+- toggle on invoice form wether price is inclusive of tax.
- retrieve available balance from AI api & show in settings/services.
- let user choose the model to use in settings/services/ai
- real error logging for OpenAI and importing in general
@@ -10,8 +10,6 @@ TODO:
- log_set_depth function so data can be grouped
- log elapsed time for ai requests
- refactor _add functions to use _import functions
-- _import functions should not check for validity and should never fail because of invalid data
-- invalid invoices should be marked in the UI
- write tests that check error handling for corrupt files. (e.g. references to tax rates, project and cost center that failed to load)
- it is possible a referenced tax rate is loaded after an invoice is loaded. This means all invoices need to be recalculated after file load. (try to write a test for this).
- invoice sequential number should be modifyable & checked for uniqueness (for external invoices being imported)
diff --git a/include/ui.hpp b/include/ui.hpp
index 0f71c1e..76b5395 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -113,4 +113,5 @@ namespace ImGui
void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country, bool has_error);
bool FormCurrencyCombo(char* currency);
void FormToggleCombo(bool *buffer, char* option1, char* option2);
+ bool DrawWarningIcon(float radius);
} \ No newline at end of file
diff --git a/manual/01_features.md b/manual/01_features.md
index 5a397dd..f26ba6a 100644
--- a/manual/01_features.md
+++ b/manual/01_features.md
@@ -1,11 +1,11 @@
# Introduction
-OpenBooks is a simple and portable accounting tool. This tool aims to be EU VAT Directive compliant and has country specific compliance, invoice keeping and tax reporting.
+OpenBooks is a simple and portable accounting tool. This tool aims to be EU VAT Directive compliant and has country specific compliance for invoice keeping and tax reporting.
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 export/send 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 tax reports and audit files (see supported countries list).
- OpenBooks can create and export income statements.
diff --git a/src/administration.cpp b/src/administration.cpp
index 495f847..6e6cfb4 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -799,9 +799,6 @@ void administration::company_info_set(contact data)
// =======================
a_err administration::contact_import(contact data)
{
- a_err result = administration::contact_is_valid(data);
- if (result != A_ERR_SUCCESS) return result;
-
if (strcmp(data.id, MY_COMPANY_ID) == 0)
{
administration::company_info_import(data);
@@ -1104,9 +1101,6 @@ char* administration::project_get_status_string(project data)
a_err administration::project_import(project data)
{
- a_err result = administration::project_is_valid(data);
- if (result != A_ERR_SUCCESS) return result;
-
project* new_project = (project*)memops::alloc(sizeof(project));
if (!new_project) return A_ERR_GENERIC;
@@ -1443,9 +1437,6 @@ a_err administration::cost_center_is_valid(cost_center data)
a_err administration::cost_center_import(cost_center data)
{
- a_err result = administration::cost_center_is_valid(data);
- if (result != A_ERR_SUCCESS) return result;
-
cost_center* tb = (cost_center*)memops::alloc(sizeof(cost_center));
if (!tb) return A_ERR_GENERIC;
@@ -1701,9 +1692,6 @@ a_err administration::invoice_update(invoice* inv)
a_err administration::invoice_import(invoice* inv)
{
- //a_err result = administration::invoice_is_valid(inv);
- //if (result != A_ERR_SUCCESS) return result;
-
inv->is_triangulation = !(memcmp(&inv->addressee.address, &inv->customer.address, sizeof(address)) == 0);
inv->issued_at -= (inv->issued_at % 86400);
diff --git a/src/locales/en.cpp b/src/locales/en.cpp
index e68e930..8ab4eaf 100644
--- a/src/locales/en.cpp
+++ b/src/locales/en.cpp
@@ -24,6 +24,9 @@ locale_entry en_locales[] = {
{"ui.invoiceRequirementP2", "needs to be completed before adding invoices."},
{"ui.next", "Next >>"},
{"ui.prev", "<< Prev"},
+ {"ui.tooltip.invalidInvoice", "Invoice has missing information."},
+ {"ui.tooltip.invalidProject", "Project has missing information."},
+ {"ui.tooltip.invalidContact", "Contact has missing information."},
// Status strings.
{"status.saved", "[Saved to disk]"},
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp
index ee0bebb..0bfd5c8 100644
--- a/src/ui/imgui_extensions.cpp
+++ b/src/ui/imgui_extensions.cpp
@@ -470,4 +470,40 @@ namespace ImGui
ImGui::EndCombo();
}
}
+
+ bool DrawWarningIcon(float radius)
+ {
+ ImGui::SameLine();
+
+ ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
+ ImVec2 center = ImVec2(cursor_pos.x + radius, cursor_pos.y + radius + 4);
+
+ ImGui::PushID((int)center.y);
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+
+ // Draw red circle
+ draw_list->AddCircleFilled(center, radius, IM_COL32(255, 0, 0, 255));
+
+ // Draw exclamation mark
+ float line_height = radius * 0.9f;
+ float line_thickness = radius * 0.3f;
+ float dot_radius = radius * 0.2f;
+
+ ImVec2 line_start(center.x - line_thickness/3.0f, center.y - line_height * 0.7f);
+ ImVec2 line_end(center.x - line_thickness/3.0f, center.y + line_height * 0.1f);
+
+ // Vertical bar
+ draw_list->AddLine(line_start, line_end, IM_COL32(255, 255, 255, 255), line_thickness);
+
+ // Bottom dot
+ draw_list->AddCircleFilled(ImVec2(center.x - dot_radius/3.0f, center.y + line_height * 0.55f), dot_radius, IM_COL32(255, 255, 255, 255));
+
+ ImGui::SetCursorScreenPos(ImVec2(center.x - radius, center.y - radius));
+ ImGui::InvisibleButton("##warning_icon", ImVec2(radius * 2, radius * 2));
+ ImGui::PopID();
+ if (ImGui::IsItemHovered()) {
+ return true;
+ }
+ return false;
+ }
} \ No newline at end of file
diff --git a/src/ui/ui_contacts.cpp b/src/ui/ui_contacts.cpp
index 7d585c9..30d01de 100644
--- a/src/ui/ui_contacts.cpp
+++ b/src/ui/ui_contacts.cpp
@@ -154,6 +154,14 @@ static void draw_contact_list()
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text(c.id);
+
+ if (administration::contact_is_valid(c) != A_ERR_SUCCESS)
+ {
+ if (ImGui::DrawWarningIcon(8.0f)) {
+ ImGui::SetTooltip(locale::get("ui.tooltip.invalidContact"));
+ }
+ }
+
ImGui::TableSetColumnIndex(1); ImGui::Text(c.name);
ImGui::TableSetColumnIndex(2); ImGui::Text("%s %s", c.address.address1, c.address.address2);
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index e22b5b1..b3c878a 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -228,6 +228,14 @@ static void draw_expenses_list()
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text(c.sequential_number);
+
+ if (administration::invoice_is_valid(&c) != A_ERR_SUCCESS)
+ {
+ if (ImGui::DrawWarningIcon(8.0f)) {
+ ImGui::SetTooltip(locale::get("ui.tooltip.invalidInvoice"));
+ }
+ }
+
ImGui::TableSetColumnIndex(1); ImGui::Text(c.supplier.name);
ImGui::TableSetColumnIndex(2); ImGui::Text(c.customer.name);
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 850c9a3..fbc6eb0 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -311,7 +311,7 @@ static void draw_invoices_list()
if (ImGui::BeginTable("TableInvoices", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
- ImGui::TableSetupColumn(locale::get("invoice.table.invoicenumber"), ImGuiTableColumnFlags_WidthFixed, 120);
+ ImGui::TableSetupColumn(locale::get("invoice.table.invoicenumber"), ImGuiTableColumnFlags_WidthFixed, 130);
ImGui::TableSetupColumn(locale::get("invoice.table.customer"));
ImGui::TableSetupColumn(locale::get("invoice.table.addressee"));
ImGui::TableSetupColumn(locale::get("invoice.table.issuedat"));
@@ -325,6 +325,14 @@ static void draw_invoices_list()
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text(c.sequential_number);
+
+ if (administration::invoice_is_valid(&c) != A_ERR_SUCCESS)
+ {
+ if (ImGui::DrawWarningIcon(8.0f)) {
+ ImGui::SetTooltip(locale::get("ui.tooltip.invalidInvoice"));
+ }
+ }
+
ImGui::TableSetColumnIndex(1); ImGui::Text(c.customer.name);
ImGui::TableSetColumnIndex(2); ImGui::Text(c.addressee.name);
diff --git a/src/ui/ui_projects.cpp b/src/ui/ui_projects.cpp
index 66cc336..43a8eb4 100644
--- a/src/ui/ui_projects.cpp
+++ b/src/ui/ui_projects.cpp
@@ -134,6 +134,14 @@ static void draw_project_list()
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text(c.id);
+
+ if (administration::project_is_valid(c) != A_ERR_SUCCESS)
+ {
+ if (ImGui::DrawWarningIcon(8.0f)) {
+ ImGui::SetTooltip(locale::get("ui.tooltip.invalidInvoice"));
+ }
+ }
+
ImGui::TableSetColumnIndex(1); ImGui::Text(locale::get(administration::project_get_status_string(c)));
ImGui::TableSetColumnIndex(2); ImGui::Text(c.description);