diff options
| author | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-08-09 17:40:03 +0200 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-08-09 17:40:03 +0200 |
| commit | 7a29dfbc37f2440b7e5461e905651b25615d2d02 (patch) | |
| tree | 838997dbd968ef787a309ec980a18e0a74809585 | |
| parent | 66d21e5fef32f64219f0f0b86c3b4b4a74623ba1 (diff) | |
editing and adding of tax brackets and cost centers
| -rw-r--r-- | NOTES.md | 9 | ||||
| -rw-r--r-- | include/administration.hpp | 3 | ||||
| -rw-r--r-- | src/administration.cpp | 58 | ||||
| -rw-r--r-- | src/ui/ui_settings.cpp | 172 |
4 files changed, 212 insertions, 30 deletions
@@ -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; |
