summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/CHANGES.rst3
-rw-r--r--include/administration.hpp5
-rw-r--r--include/ui.hpp3
-rw-r--r--src/administration.cpp17
-rw-r--r--src/administration_writer.cpp1
-rw-r--r--src/ui/imgui_extensions.cpp30
-rw-r--r--src/ui/ui_expenses.cpp2
-rw-r--r--src/ui/ui_invoices.cpp7
8 files changed, 56 insertions, 12 deletions
diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst
index 8701d77..2bb8399 100644
--- a/docs/CHANGES.rst
+++ b/docs/CHANGES.rst
@@ -1,9 +1,8 @@
.. _changes:
TODO:
-- get rid of billing item properties: invoice_id and tax_rate_id. replace tax_rate_id with copy of tax rate
+- write tests that check error handling for corrupt files. (e.g. references to tax rates, project and cost center that failed to load)
- get rid of customer_id and supplier_id. Just check for existing contact entries when finishing invoice and create new contact if necessary.
-- validate billing items in invoice add/update
- project start and end date should be stored as YYYY-MM-DD
- Send invoice by email
- create invoice from PDF file
diff --git a/include/administration.hpp b/include/administration.hpp
index 0d15195..e2ff80d 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -121,7 +121,6 @@ typedef enum
typedef struct
{
char id[MAX_LEN_ID]; // B/[id]
- char invoice_id[MAX_LEN_ID]; // I/[id]
float amount;
bool amount_is_percentage;
char description[MAX_LEN_LONG_DESC];
@@ -368,6 +367,8 @@ typedef struct
#define A_ERR_MAX_ITEMS_REACHED (1ULL << 15)
#define A_ERR_MISSING_CODE (1ULL << 16)
#define A_ERR_MISSING_EMAIL (1ULL << 17)
+#define A_ERR_MISSING_TAX_RATE (1ULL << 18)
+#define A_ERR_INVALID_BILLING_ITEM (1ULL << 19)
typedef uint32_t a_err;
@@ -497,4 +498,6 @@ a_err administration_billing_item_add_to_invoice(invoice* invoice, billing_ite
a_err administration_billing_item_update_in_invoice(invoice* invoice, billing_item item);
a_err administration_billing_item_remove_from_invoice(invoice* invoice, billing_item item);
+a_err administration_billing_item_is_valid(billing_item item);
+
u32 administration_billing_item_get_all_for_invoice(invoice* invoice, billing_item* buffer); \ No newline at end of file
diff --git a/include/ui.hpp b/include/ui.hpp
index 3a8ce66..ebfa210 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -95,6 +95,7 @@ void ui_destroy_earnings();
// Custom imgui widgets.
namespace ImGui
{
+ void InputTextWithError(const char* text, char* buffer, size_t buf_size, bool has_error);
int TextInputWithAutocomplete(const char* hint, char* buffer, size_t buf_size, char** suggestions, int suggestion_count, bool has_error);
void FormContactAutocomplete(contact* buffer, bool has_error);
@@ -103,7 +104,7 @@ namespace ImGui
void FormContactTypeCombo(contact_type* type);
void FormCostCenterCombo(char* costcenter_id);
void FormProjectCombo(char* project_id);
- void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country);
+ 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);
} \ No newline at end of file
diff --git a/src/administration.cpp b/src/administration.cpp
index 09bb560..690fcd9 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -1633,6 +1633,13 @@ a_err administration_invoice_is_valid(invoice* invoice)
if (administration_contact_is_valid(invoice->customer) != A_ERR_SUCCESS) result |= A_ERR_INVALID_CUSTOMER;
if (administration_contact_is_valid(invoice->supplier) != A_ERR_SUCCESS) result |= A_ERR_INVALID_SUPPLIER;
+ list_iterator_start(&invoice->billing_items);
+ while (list_iterator_hasnext(&invoice->billing_items)) {
+ billing_item* c = (billing_item *)list_iterator_next(&invoice->billing_items);
+ if (administration_billing_item_is_valid(*c) != A_ERR_SUCCESS) result |= A_ERR_INVALID_BILLING_ITEM;
+ }
+ list_iterator_stop(&invoice->billing_items);
+
return result;
}
@@ -2049,6 +2056,15 @@ a_err administration_billing_item_update_in_invoice(invoice* invoice, billing_it
return A_ERR_NOT_FOUND;
}
+a_err administration_billing_item_is_valid(billing_item item)
+{
+ a_err result = A_ERR_SUCCESS;
+ if (strlen(item.description) == 0) result |= A_ERR_MISSING_DESCRIPTION;
+ if (strlen(item.tax_rate_id) == 0) result |= A_ERR_MISSING_TAX_RATE;
+
+ return result;
+}
+
a_err administration_billing_item_add_to_invoice(invoice* invoice, billing_item item)
{
if (administration_billing_item_count(invoice) >= MAX_BILLING_ITEMS) return A_ERR_MAX_ITEMS_REACHED;
@@ -2058,7 +2074,6 @@ a_err administration_billing_item_add_to_invoice(invoice* invoice, billing_item
memcpy(tb, &item, sizeof(billing_item));
snprintf(tb->id, sizeof(tb->id), "B/%d", administration_create_id());
- strops_copy(tb->invoice_id, invoice->id, sizeof(tb->invoice_id));
strops_copy(tb->currency, invoice->currency, MAX_LEN_CURRENCY); // Set billing item currency to invoice currency.
administration_recalculate_billing_item_totals(tb);
diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp
index e7d9e07..e02d476 100644
--- a/src/administration_writer.cpp
+++ b/src/administration_writer.cpp
@@ -308,7 +308,6 @@ bool administration_writer_save_invoice_blocking(invoice inv)
// These can all be retrieved from existing contacts.
// properties not stored from billing item:
- // - invoice_id (not necessary)
// - discount (can be recalculated from line_amount - (quantity * unit_price) )
// - tax (can be recalculated)
// - total (can be recalculated)
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp
index 1875c22..8485dbf 100644
--- a/src/ui/imgui_extensions.cpp
+++ b/src/ui/imgui_extensions.cpp
@@ -9,6 +9,19 @@
namespace ImGui
{
+ void InputTextWithError(const char* text, char* buffer, size_t buf_size, bool has_error)
+ {
+ if (has_error) {
+ ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f);
+ }
+ ImGui::InputText(text, buffer, buf_size);
+ if (has_error) {
+ ImGui::PopStyleVar();
+ ImGui::PopStyleColor();
+ }
+ }
+
void FormInputTextWithErrorHint(const char* hint, char* buffer, size_t buf_size, bool has_error)
{
float widthAvailable = ImGui::GetContentRegionAvail().x;
@@ -272,7 +285,7 @@ namespace ImGui
free(buffer);
}
- void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country)
+ void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country, bool has_error)
{
u32 tax_rate_count = administration_tax_rate_count();
tax_rate* buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count);
@@ -307,6 +320,11 @@ namespace ImGui
else snprintf(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate);
}
+ if (has_error) {
+ ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f);
+ }
+
if (ImGui::BeginCombo("##Tax Bracket", rate_str_buf))
{
for (u32 n = 0; n < tax_rate_count; n++)
@@ -326,6 +344,11 @@ namespace ImGui
}
ImGui::EndCombo();
}
+
+ if (has_error) {
+ ImGui::PopStyleVar();
+ ImGui::PopStyleColor();
+ }
if (selected_tax_rate_index != -1) {
strops_copy(tax_rate_id, buffer[selected_tax_rate_index].id, MAX_LEN_ID);
@@ -392,7 +415,10 @@ namespace ImGui
void FormToggleCombo(bool *buffer, char* option1, char* option2)
{
const char* items[] = { option1, option2 };
- if (ImGui::BeginCombo("Mode", items[*buffer])) {
+
+ char ID[MAX_LEN_LONG_DESC];
+ snprintf(ID, MAX_LEN_LONG_DESC, "Mode##%p", buffer);
+ if (ImGui::BeginCombo(ID, items[*buffer])) {
for (int n = 0; n < 2; n++) {
bool is_selected = (n == (int)*buffer);
if (ImGui::Selectable(items[n], is_selected)) {
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index 7d94eaf..7dd5add 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -204,7 +204,7 @@ static void ui_draw_expenses_list()
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text(c.sequential_number);
ImGui::TableSetColumnIndex(1); ImGui::Text(c.supplier.name);
- ImGui::TableSetColumnIndex(2); ImGui::Text(c.addressee.name);
+ ImGui::TableSetColumnIndex(2); ImGui::Text(c.customer.name);
struct tm *lt = localtime(&c.issued_at);
char buf[80];
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index f061e3f..6c3e229 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -73,6 +73,7 @@ void draw_invoice_items_form(invoice* invoice)
for (u32 i = 0; i < invoice_items; i++)
{
billing_item item = buffer[i];
+ a_err valid = administration_billing_item_is_valid(item);
ImGui::TableNextRow();
@@ -92,7 +93,7 @@ void draw_invoice_items_form(invoice* invoice)
ImGui::TableSetColumnIndex(2);
ImGui::PushItemWidth(-1);
- ImGui::InputText("##desc", item.description, IM_ARRAYSIZE(item.description));
+ ImGui::InputTextWithError("##desc", item.description, IM_ARRAYSIZE(item.description), valid & A_ERR_MISSING_DESCRIPTION);
ImGui::PopItemWidth();
ImGui::TableSetColumnIndex(3);
@@ -104,14 +105,14 @@ void draw_invoice_items_form(invoice* invoice)
ImGui::InputFloat("##discount", &item.discount, 0.0f, 0.0f, "%.2f");
ImGui::SameLine();
- ImGui::FormToggleCombo(&item.amount_is_percentage, item.currency, "%");
+ ImGui::FormToggleCombo(&item.discount_is_percentage, item.currency, "%");
ImGui::TableSetColumnIndex(5);
ImGui::Text("%.2f %s", item.net, item.currency);
ImGui::TableSetColumnIndex(6);
ImGui::PushItemWidth(-1);
- ImGui::FormTaxRateCombo(item.tax_rate_id, invoice->customer.address.country_code, invoice->supplier.address.country_code);
+ ImGui::FormTaxRateCombo(item.tax_rate_id, invoice->customer.address.country_code, invoice->supplier.address.country_code, valid & A_ERR_MISSING_TAX_RATE);
ImGui::PopItemWidth();