summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-09 17:40:03 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-09 17:40:03 +0200
commit7a29dfbc37f2440b7e5461e905651b25615d2d02 (patch)
tree838997dbd968ef787a309ec980a18e0a74809585
parent66d21e5fef32f64219f0f0b86c3b4b4a74623ba1 (diff)
editing and adding of tax brackets and cost centers
-rw-r--r--NOTES.md9
-rw-r--r--include/administration.hpp3
-rw-r--r--src/administration.cpp58
-rw-r--r--src/ui/ui_settings.cpp172
4 files changed, 212 insertions, 30 deletions
diff --git a/NOTES.md b/NOTES.md
index 9c1b419..1477ed1 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -122,4 +122,11 @@ administration.money/
|-|-|
| id `auto` | reference id `E/[id]` |
| code `required` | Internal code of cost center |
-| description `required` | Description of cost center | \ No newline at end of file
+| description `required` | Description of cost center |
+
+| Tax bracket||
+|-|-|
+| id `auto` | reference id `T/[id]` |
+| country code `required` | 2 letter country code |
+| description `required` | Description of tax bracket |
+| rate `required` | Tax rate % | \ No newline at end of file
diff --git a/include/administration.hpp b/include/administration.hpp
index 196e674..b567f6f 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -5,6 +5,7 @@
typedef struct
{
+ char id[16];
char country_code[3];
float rate; // 0-100
char description[32];
@@ -145,6 +146,8 @@ u32 administration_get_projects(u32 page_index, u32 page_size, project* buff
u32 administration_get_tax_bracket_count();
u32 administration_get_tax_brackets(country_tax_bracket* buffer);
+bool administration_add_tax_bracket(country_tax_bracket data);
+bool administration_update_tax_bracket(country_tax_bracket data);
u32 administration_get_cost_center_count();
u32 administration_get_cost_centers(cost_center* buffer);
diff --git a/src/administration.cpp b/src/administration.cpp
index e41942e..8a0b104 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -9,17 +9,27 @@
administration g_administration;
-static void administration_create_default_tax_brackets()
+#define ADD_BRACKET(_country, _rate, _description)\
+{\
+ country_tax_bracket* tb = (country_tax_bracket*)malloc(sizeof(country_tax_bracket));\
+ snprintf(tb->id, sizeof(tb->id), "T/%d", administration_create_id());\
+ memcpy(tb->country_code, _country, sizeof(tb->country_code));\
+ tb->rate = _rate;\
+ memcpy(tb->description, _description, sizeof(tb->description));\
+ list_append(&g_administration.tax_brackets, tb);\
+ g_administration.next_id++;\
+}
+
+static int compare_tax_countries(const void *a, const void *b)
{
- #define ADD_BRACKET(_country, _rate, _description)\
- {\
- country_tax_bracket* tb = (country_tax_bracket*)malloc(sizeof(country_tax_bracket));\
- memcpy(tb->country_code, _country, sizeof(tb->country_code));\
- tb->rate = _rate;\
- memcpy(tb->description, _description, sizeof(tb->description));\
- list_append(&g_administration.tax_brackets, tb);\
- }
+ country_tax_bracket *objA = (country_tax_bracket *)a;
+ country_tax_bracket *objB = (country_tax_bracket *)b;
+ return strcmp(objA->country_code, objB->country_code);
+}
+
+static void administration_create_default_tax_brackets()
+{
// General brackets shared between countries.
ADD_BRACKET("00", 0.0f, "tax.reverse_charge");
ADD_BRACKET("00", 0.0f, "tax.exempt");
@@ -155,6 +165,9 @@ static void administration_create_default_tax_brackets()
ADD_BRACKET("SE", 25.0f, "tax.standard");
ADD_BRACKET("SE", 6.0f, "tax.reduced");
ADD_BRACKET("SE", 12.0f, "tax.reduced");
+
+ list_attributes_comparator(&g_administration.tax_brackets, compare_tax_countries);
+ list_sort(&g_administration.tax_brackets, -1);
}
static void administration_create_default_cost_centers()
@@ -400,6 +413,14 @@ u32 administration_get_tax_bracket_count()
return list_size(&g_administration.tax_brackets);
}
+bool administration_add_tax_bracket(country_tax_bracket data)
+{
+ ADD_BRACKET(data.country_code, data.rate, "");
+ list_attributes_comparator(&g_administration.tax_brackets, compare_tax_countries);
+ list_sort(&g_administration.tax_brackets, -1);
+ return true;
+}
+
u32 administration_get_tax_brackets(country_tax_bracket* buffer)
{
assert(buffer);
@@ -416,6 +437,25 @@ u32 administration_get_tax_brackets(country_tax_bracket* buffer)
return write_cursor;
}
+bool administration_update_tax_bracket(country_tax_bracket data)
+{
+ 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, data.id) == 0) {
+ memcpy(c, &data, sizeof(data));
+ list_iterator_stop(&g_administration.tax_brackets);
+ return true;
+ }
+ }
+ list_iterator_stop(&g_administration.tax_brackets);
+
+ return false;
+}
+
+
+
u32 administration_get_cost_center_count()
{
return list_size(&g_administration.cost_centers);
diff --git a/src/ui/ui_settings.cpp b/src/ui/ui_settings.cpp
index 7854be3..c2c34b9 100644
--- a/src/ui/ui_settings.cpp
+++ b/src/ui/ui_settings.cpp
@@ -38,25 +38,39 @@ void ui_setup_settings()
static void ui_draw_vat_rates()
{
+ static bool is_adding_item = false;
+ static country_tax_bracket new_tax_bracket;
+
+ static bool is_editing_item = false;
+ static u32 editing_item_index = 0;
+
if (ImGui::BeginTable("TableVatRates", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn(localize("settings.vat.table.country"), ImGuiTableColumnFlags_WidthFixed, 140);
ImGui::TableSetupColumn(localize("settings.vat.table.rates"));
+ // Used to generate headers for each individual country.
char prev_country[3];
prev_country[0] = 0;
for (u32 i = 0; i < tax_bracket_count; i++) {
country_tax_bracket c = tax_brackets[i];
+ // Set to false for shared rates.
+ bool can_be_modified = true;
+
+ // Check for fixed rates shared accross countries.
if (strcmp(c.country_code, "00") == 0)
{
- strops_copy(prev_country, c.country_code, 3);
+ strops_copy(prev_country, c.country_code, 3);
+ can_be_modified = false;
}
+ // Generate headers per country.
else if (strcmp(c.country_code, prev_country) != 0)
- {
+ {
strops_copy(prev_country, c.country_code, 3);
+ // Empty row.
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("");
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(69, 69, 69, 255));
@@ -67,13 +81,108 @@ static void ui_draw_vat_rates()
char locale_buf[20];
snprintf(locale_buf, sizeof(locale_buf), "country.%s", c.country_code);
- ImGui::TableSetColumnIndex(0); ImGui::Text(localize(locale_buf));
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text(localize(locale_buf));
+
+ // If not adding an item already, show + button next to country name.
+ if (!is_adding_item)
+ {
+ ImGui::SameLine();
+ char btn_name[20];
+ snprintf(btn_name, sizeof(btn_name), "+##%d",i);
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0));
+ if (ImGui::Button(btn_name, ImVec2(20,20))) {
+ is_adding_item = true;
+ is_editing_item = false;
+ memset(&new_tax_bracket, 0, sizeof(new_tax_bracket));
+ strops_copy(new_tax_bracket.country_code, c.country_code, 3);
+ }
+ ImGui::PopStyleVar();
+ }
+
ImGui::TableSetColumnIndex(1); ImGui::Text("");
}
+ // Column 1: description of tax bracket. Is only displayed on shared tax brackets for clarity.
ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(0); ImGui::Text(localize(c.description));
- ImGui::TableSetColumnIndex(1); ImGui::Text("%.2f%%", c.rate);
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text(can_be_modified ? "" : localize(c.description));
+
+
+ // Column 2: When editing, show input for new rate. Else we display the stored rate and check for modify request.
+ ImGui::TableSetColumnIndex(1);
+ if (is_editing_item && editing_item_index == i)
+ {
+ ImGui::InputFloat("##Rate", &new_tax_bracket.rate, 1.0f, 5.0f, "%.2f");
+
+ if (new_tax_bracket.rate < 0.0f) new_tax_bracket.rate = 0.0f;
+ if (new_tax_bracket.rate > 100.0f) new_tax_bracket.rate = 100.0f;
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.save"))) {
+ is_editing_item = false;
+ is_adding_item = false;
+
+ administration_update_tax_bracket(new_tax_bracket);
+
+ ui_destroy_settings();
+ ui_setup_settings();
+ }
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.cancel"))) {
+ is_editing_item = false;
+ is_adding_item = false;
+ memset(&new_tax_bracket, 0, sizeof(new_tax_bracket));
+ }
+ }
+ else
+ {
+ ImGui::Text("%.2f%%", c.rate);
+
+ if (can_be_modified && ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
+ {
+ is_editing_item = true;
+ is_adding_item = false;
+ editing_item_index = i;
+ new_tax_bracket = c;
+ }
+ }
+
+ // When adding a new entry it is displayed at the bottom of the list of the country we are adding to.
+ // Check for end of list (for last country in the list), or check if next country differs from current country.
+ // If it is different we have reached the end of the list for the current country.
+ if (i == tax_bracket_count-1 || (i < tax_bracket_count-1 && strcmp(tax_brackets[i+1].country_code, c.country_code) != 0))
+ {
+ if (is_adding_item && strcmp(new_tax_bracket.country_code, prev_country) == 0)
+ {
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0); ImGui::Text("");
+ ImGui::TableSetColumnIndex(1);
+ ImGui::InputFloat("##Rate", &new_tax_bracket.rate, 1.0f, 5.0f, "%.2f");
+
+ if (new_tax_bracket.rate < 0.0f) new_tax_bracket.rate = 0.0f;
+ if (new_tax_bracket.rate > 100.0f) new_tax_bracket.rate = 100.0f;
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.save"))) {
+ is_editing_item = false;
+ is_adding_item = false;
+
+ administration_add_tax_bracket(new_tax_bracket);
+
+ ui_destroy_settings();
+ ui_setup_settings();
+ }
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.cancel"))) {
+ is_editing_item = false;
+ is_adding_item = false;
+ memset(&new_tax_bracket, 0, sizeof(new_tax_bracket));
+ }
+ }
+ }
}
ImGui::EndTable();
@@ -97,14 +206,18 @@ static void ui_draw_cost_centers()
cost_center c = cost_centers[i];
ImGui::TableNextRow();
+
+ // Column 1: Code of cost center.
ImGui::TableSetColumnIndex(0); ImGui::Text(c.code);
- ImGui::TableSetColumnIndex(1);
-
+
+ // Column 2: When editing, show inputs for new description. Else show stored description and check for modify request.
+ ImGui::TableSetColumnIndex(1);
if (is_editing_item && editing_item_index == i)
{
- ImGui::InputText("##Description", new_cost_center.description, IM_ARRAYSIZE(new_cost_center.description));
-
bool is_desc_valid = administration_verify_cost_center_description(new_cost_center.description);
+ if (!is_desc_valid) ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(105, 43, 43, 255));
+ ImGui::InputText("##Description", new_cost_center.description, IM_ARRAYSIZE(new_cost_center.description));
+ if (!is_desc_valid) ImGui::PopStyleColor();
if (!is_desc_valid) ImGui::BeginDisabled();
ImGui::SameLine();
@@ -113,37 +226,48 @@ static void ui_draw_cost_centers()
is_adding_item = false;
administration_update_cost_center(new_cost_center);
+ memset(&new_cost_center, 0, sizeof(new_cost_center));
ui_destroy_settings();
ui_setup_settings();
}
if (!is_desc_valid) ImGui::EndDisabled();
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.cancel"))) {
+ is_editing_item = false;
+ is_adding_item = false;
+ memset(&new_cost_center, 0, sizeof(new_cost_center));
+ }
}
else
{
ImGui::Text(localize(c.description));
- }
- if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
- {
- is_editing_item = true;
- is_adding_item = false;
- editing_item_index = i;
- new_cost_center = c;
- }
+ if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
+ {
+ is_editing_item = true;
+ is_adding_item = false;
+ editing_item_index = i;
+ new_cost_center = c;
+ }
+ }
}
+ // When adding a new item. Show inputs for code and description, check validity, and handle save/cancel.
+ // Form for new entry is displayed at bottom of list.
if (is_adding_item)
{
ImGui::TableNextRow();
bool is_code_valid = administration_verify_cost_center_code(new_cost_center.code);
- if (!is_code_valid) ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(255, 0, 0, 64));
- ImGui::TableSetColumnIndex(0); ImGui::InputText("##Code", new_cost_center.code, IM_ARRAYSIZE(new_cost_center.code));
+ if (!is_code_valid) ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(105, 43, 43, 255));
+ ImGui::TableSetColumnIndex(0); ImGui::InputText("##Code", new_cost_center.code, IM_ARRAYSIZE(new_cost_center.code),
+ ImGuiInputTextFlags_CharsUppercase|ImGuiInputTextFlags_CharsNoBlank);
if (!is_code_valid) ImGui::PopStyleColor();
bool is_desc_valid = administration_verify_cost_center_description(new_cost_center.description);
- if (!is_desc_valid) ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(255, 0, 0, 64));
+ if (!is_desc_valid) ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(105, 43, 43, 255));
ImGui::TableSetColumnIndex(1); ImGui::InputText("##Description", new_cost_center.description, IM_ARRAYSIZE(new_cost_center.description));
if (!is_desc_valid) ImGui::PopStyleColor();
@@ -161,11 +285,19 @@ static void ui_draw_cost_centers()
ui_setup_settings();
}
if (!can_save) ImGui::EndDisabled();
+
+ ImGui::SameLine();
+ if (ImGui::Button(localize("form.cancel"))) {
+ is_adding_item = false;
+ is_editing_item = false;
+ memset(&new_cost_center, 0, sizeof(new_cost_center));
+ }
}
ImGui::EndTable();
}
+ // If not adding a new item already, show create button at bottom of list.
if (!is_adding_item && ImGui::Button(localize("form.create")))
{
is_adding_item = true;