summaryrefslogtreecommitdiff
path: root/src/ui/ui_invoices.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/ui_invoices.cpp')
-rw-r--r--src/ui/ui_invoices.cpp263
1 files changed, 248 insertions, 15 deletions
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 2b5a834..036d85b 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -16,21 +16,136 @@ void ui_draw_address_form(address* buffer);
static view_state current_view_state = view_state::LIST;
static invoice active_invoice = {0};
+cost_center* cost_center_list_buffer = 0;
+country_tax_bracket* tax_bracket_list_buffer = 0;
+
void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0);
void ui_setup_invoices()
{
current_view_state = view_state::LIST;
active_invoice = administration_create_empty_invoice();
+
+ u32 costcenter_count = administration_get_cost_center_count();
+ cost_center_list_buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count); // @leak
+
+ u32 tax_bracket_count = administration_get_tax_bracket_count();
+ tax_bracket_list_buffer = (country_tax_bracket*) malloc(sizeof(country_tax_bracket) * tax_bracket_count); // @leak
+}
+
+// TODO move custom ui functions to helpers.cpp
+
+void draw_tax_bracket_selector(char* tax_bracket_id)
+{
+ country_tax_bracket* selected_tax_bracket = NULL;
+
+ country_tax_bracket* buffer = tax_bracket_list_buffer;
+ u32 tax_bracket_count = administration_get_tax_brackets_for_country(buffer, administration_get_company_info().address.country_code);
+
+ // Select tax bracket by given id.
+ if (strlen(tax_bracket_id) > 0)
+ {
+ for (u32 i = 0; i < tax_bracket_count; i++)
+ {
+ if (strcmp(buffer[i].id, tax_bracket_id) == 0)
+ {
+ selected_tax_bracket = &buffer[i];
+ break;
+ }
+ }
+ }
+
+ int selected_tax_bracket_index = -1;
+ char rate_str_buf[20];
+ rate_str_buf[0] = 0;
+ if (selected_tax_bracket)
+ {
+ if (strcmp(selected_tax_bracket->country_code, "00") == 0) snprintf(rate_str_buf, 20, "%s", localize(selected_tax_bracket->description));
+ else snprintf(rate_str_buf, 20, "%s/%.1f%%", selected_tax_bracket->country_code, selected_tax_bracket->rate);
+ }
+
+ if (ImGui::BeginCombo("##Tax Bracket", rate_str_buf))
+ {
+ for (u32 n = 0; n < tax_bracket_count; n++)
+ {
+ bool is_selected = selected_tax_bracket && strcmp(selected_tax_bracket->id, buffer[n].id) == 0;
+
+ if (strcmp(buffer[n].country_code, "00") == 0) snprintf(rate_str_buf, 20, "%s", localize(buffer[n].description));
+ else snprintf(rate_str_buf, 20, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate);
+
+ if (ImGui::Selectable(rate_str_buf, is_selected)) {
+ selected_tax_bracket_index = n;
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (selected_tax_bracket_index != -1) {
+ strops_copy(tax_bracket_id, buffer[selected_tax_bracket_index].id, 16);
+ }
+}
+
+bool draw_currency_selector(char* currency)
+{
+ int currentCurrency = 0;
+ bool result = false;
+
+ // Top 15 most traded currencies + all EU official currencies
+ const char* currencies[] = {
+ // Top 15
+ "EUR", "USD", "JPY", "GBP", "AUD", "CAD", "CHF",
+ "CNY", "HKD", "NZD", "SEK", "KRW", "SGD", "NOK", "MXN",
+
+ // Additional EU currencies
+ "BGN", // Bulgarian Lev
+ "CZK", // Czech Koruna
+ "DKK", // Danish Krone
+ "HUF", // Hungarian Forint
+ "PLN", // Polish Zloty
+ "RON", // Romanian Leu
+ // "HRK", // Croatian Kuna (legacy, replaced by EUR in 2023)
+ };
+ int currency_count = sizeof(currencies) / sizeof(char*);
+
+ if (strlen(currency) > 0)
+ {
+ for (int i = 0; i < currency_count; i++)
+ {
+ if (strcmp(currencies[i], currency) == 0)
+ {
+ currentCurrency = i;
+ break;
+ }
+ }
+ }
+
+ ImGui::SetNextItemWidth(100.0f);
+ if (ImGui::BeginCombo("##currency", currencies[currentCurrency]))
+ {
+ for (int n = 0; n < IM_ARRAYSIZE(currencies); n++)
+ {
+ bool isSelected = (currentCurrency == n);
+ if (ImGui::Selectable(currencies[n], isSelected))
+ {
+ result = true;
+ strops_copy(currency, currencies[n], CURRENCY_LENGTH);
+ }
+
+ if (isSelected)
+ ImGui::SetItemDefaultFocus();
+ }
+ ImGui::EndCombo();
+ }
+
+ return result;
}
void draw_costcenter_selector(char* costcenter_id)
{
cost_center* selected_costcenter = NULL;
- u32 costcenter_count = administration_get_cost_center_count();
- cost_center* buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count);
- costcenter_count = administration_get_cost_centers(buffer);
+ cost_center* buffer = cost_center_list_buffer;
+ u32 costcenter_count = administration_get_cost_centers(buffer);
// Select cost center by given id.
if (strlen(costcenter_id) > 0)
@@ -61,8 +176,6 @@ void draw_costcenter_selector(char* costcenter_id)
if (selected_costcenter_index != -1) {
strops_copy(costcenter_id, buffer[selected_costcenter_index].id, 16);
}
-
- free(buffer);
}
void draw_project_selector(char* project_id)
@@ -106,9 +219,105 @@ void draw_project_selector(char* project_id)
free(buffer);
}
+static void draw_invoice_items_form(invoice* invoice)
+{
+ u32 invoice_items = administration_get_billing_items_count(invoice);
+ billing_item* buffer = (billing_item*)malloc(sizeof(billing_item) * invoice_items);
+ administration_get_all_billing_items_for_invoice(invoice, buffer);
+
+ if (ImGui::BeginTable("TableBillingItems", 8, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
+
+ ImGui::TableSetupColumn("Amount", ImGuiTableColumnFlags_WidthFixed, 80);
+ ImGui::TableSetupColumn("Description");
+ ImGui::TableSetupColumn("Price", ImGuiTableColumnFlags_WidthFixed, 100);
+ ImGui::TableSetupColumn("Discount", ImGuiTableColumnFlags_WidthFixed, 100);
+ ImGui::TableSetupColumn("Net", ImGuiTableColumnFlags_WidthFixed, 100);
+ ImGui::TableSetupColumn("Tax %", ImGuiTableColumnFlags_WidthFixed, 170);
+ ImGui::TableSetupColumn("Tax", ImGuiTableColumnFlags_WidthFixed, 100);
+ ImGui::TableSetupColumn("Total", ImGuiTableColumnFlags_WidthFixed, 100);
+
+ //ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100);
+ ImGui::TableHeadersRow();
+
+ for (u32 i = 0; i < invoice_items; i++)
+ {
+ billing_item item = buffer[i];
+
+ ImGui::TableNextRow();
+
+ ImGui::PushID(i);
+
+ ImGui::TableSetColumnIndex(0);
+ ImGui::InputFloat("##amount", &item.amount, 0.0f, 0.0f, "%.0f");
+ ImGui::SameLine();
+
+ // Toggle between X and %
+ {
+ const char* items[] = { "X", "%" };
+ if (ImGui::BeginCombo("Mode", items[item.amount_is_percentage])) {
+ for (int n = 0; n < 2; n++) {
+ bool is_selected = (n == (int)item.amount_is_percentage);
+ if (ImGui::Selectable(items[n], is_selected)) {
+ item.amount_is_percentage = n;
+ }
+ if (is_selected) {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+ }
+
+ ImGui::TableSetColumnIndex(1);
+ ImGui::InputText("##desc", item.description, IM_ARRAYSIZE(item.description));
+
+ ImGui::TableSetColumnIndex(2);
+ ImGui::InputFloat("##price", &item.net_per_item, 0.0f, 0.0f, "%.2f");
+
+ ImGui::TableSetColumnIndex(3);
+ ImGui::InputFloat("##discount", &item.discount, 0.0f, 0.0f, "%.0f");
+ ImGui::SameLine();
+
+ // Toggle between currency and %
+ {
+ const char* items[] = { item.currency, "%" };
+ if (ImGui::BeginCombo("Mode##discountMode", items[item.discount_is_percentage])) {
+ for (int n = 0; n < 2; n++) {
+ bool is_selected = (n == (int)item.discount_is_percentage);
+ if (ImGui::Selectable(items[n], is_selected)) {
+ item.discount_is_percentage = n;
+ }
+ if (is_selected) {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+ }
+
+ ImGui::TableSetColumnIndex(4);
+ ImGui::Text("%.2f %s", item.net, item.currency);
+
+ ImGui::TableSetColumnIndex(5);
+ draw_tax_bracket_selector(item.tax_bracket_id);
+
+ ImGui::TableSetColumnIndex(6);
+ ImGui::Text("%.2f %s", item.tax, item.currency);
+
+ ImGui::TableSetColumnIndex(7);
+ ImGui::Text("%.2f %s", item.total, item.currency);
+
+ ImGui::PopID();
+
+ administration_update_billing_item_of_invoice(invoice, item);
+ }
+
+ ImGui::EndTable();
+ }
+}
+
void draw_invoice_form(invoice* buffer, bool viewing_only = false)
{
- //float widthAvailable = ImGui::GetContentRegionAvail().x;
ImGui::BeginDisabled();
// 1. Identifier
@@ -122,7 +331,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
// 3. Supplier (you)
ImGui::Text("Supplier: %s", buffer->supplier.name);
- // 6. Invoice issued at
+ // 4. Invoice issued at
ImGui::BeginDisabled();
tm issued_at_date = *gmtime(&buffer->issued_at);
if (ImGui::DatePicker("##issuedAt", issued_at_date))
@@ -133,7 +342,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::Text("Invoice issued at");
ImGui::EndDisabled();
- // 7. Invoice expires at
+ // 5. Invoice expires at
ImGui::BeginDisabled();
tm expires_at_date = *gmtime(&buffer->expires_at);
if (ImGui::DatePicker("##expiresAt", expires_at_date))
@@ -144,7 +353,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::Text("Invoice expires at");
ImGui::EndDisabled();
- // 8. Product/service delivered at
+ // 6. Product/service delivered at
tm delivered_at_date = *gmtime(&buffer->delivered_at);
if (ImGui::DatePicker("##deliveredAt", delivered_at_date))
{
@@ -155,7 +364,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::Separator();
- // 4. Customer information
+ // 7. Customer information
ImGui::Text("Billing information");
bool on_autocomplete;
draw_contact_form_ex(&buffer->customer, false, true, &on_autocomplete);
@@ -164,7 +373,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
strops_copy(buffer->customer_id, buffer->customer.id, sizeof(buffer->customer_id));
}
- // 5. (optional) shipping address.
+ // 8. (optional) shipping address.
static bool shipping_is_billing_addr = true;
ImGui::Checkbox("Shipping information is billing information", &shipping_is_billing_addr);
if (!shipping_is_billing_addr) {
@@ -180,9 +389,33 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
// 10. Cost center selection
draw_costcenter_selector(buffer->cost_center_id);
- //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();
+ ImGui::Separator();
+ ImGui::Spacing();
+ ImGui::Spacing();
+ ImGui::Spacing();
+
+ // 11. New billing item button.
+ if (ImGui::Button(localize("+ Billing item")))
+ {
+ billing_item item = administration_create_empty_billing_item();
+ administration_add_billing_item_to_invoice(buffer, item);
+ }
+
+ // 12. Dropdown for invoice currency.
+ ImGui::SameLine();
+ ImGui::Text("| Currency: ");
+ ImGui::SameLine();
+ if (draw_currency_selector(buffer->currency))
+ {
+ administration_invoice_set_currency(buffer, buffer->currency);
+ }
+
+ // 13. Invoice items form
+ draw_invoice_items_form(buffer);
+
+ ImGui::Separator();
+
+ // 14. Totals overview.
if (viewing_only) ImGui::EndDisabled();
}
@@ -193,7 +426,7 @@ void draw_invoices_list()
if (ImGui::Button(localize("form.create")))
{
current_view_state = view_state::CREATE;
- active_invoice = administration_create_empty_invoice();
+ active_invoice = administration_create_empty_invoice(); // @leak
active_invoice.supplier = administration_get_company_info();
strops_copy(active_invoice.supplier_id, active_invoice.supplier.id, sizeof(active_invoice.supplier_id));
}