summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/administration.hpp4
-rw-r--r--src/administration.cpp95
-rw-r--r--src/ui/ui_invoices.cpp159
3 files changed, 253 insertions, 5 deletions
diff --git a/include/administration.hpp b/include/administration.hpp
index af449bb..96d528f 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -183,10 +183,14 @@ bool administration_verify_cost_center_description(char* text);
bool administration_add_cost_center(cost_center data);
bool administration_update_cost_center(cost_center data);
+u32 administration_get_invoice_count();
invoice administration_create_empty_invoice();
void administration_invoice_set_currency(invoice* invoice, char* currency);
bool administration_is_invoice_valid(invoice* invoice);
bool administration_add_invoice(invoice* invoice);
+bool administration_update_invoice(invoice* invoice);
+u32 administration_get_invoices(u32 page_index, u32 page_size, invoice* buffer);
+char* administration_get_invoice_status_string(invoice* invoice);
billing_item administration_create_empty_billing_item();
u32 administration_get_all_billing_items_for_invoice(invoice* invoice, billing_item* buffer);
diff --git a/src/administration.cpp b/src/administration.cpp
index 6ed7d63..95ae705 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -480,6 +480,23 @@ bool administration_is_project_valid(project data)
return strlen(data.description) > 0;
}
+char* administration_get_invoice_status_string(invoice* invoice)
+{
+ switch(invoice->status)
+ {
+ case invoice_status::INVOICE_CONCEPT: return "invoice.state.concept";
+ case invoice_status::INVOICE_SENT: return "invoice.state.sent";
+ case invoice_status::INVOICE_REMINDED: return "invoice.state.reminded";
+ case invoice_status::INVOICE_PAID: return "invoice.state.paid";
+ case invoice_status::INVOICE_EXPIRED: return "invoice.state.expired";
+ case invoice_status::INVOICE_CANCELLED: return "invoice.state.cancelled";
+ case invoice_status::INVOICE_REFUNDED: return "invoice.state.refunded";
+ case invoice_status::INVOICE_CORRECTED: return "invoice.state.corrected";
+ default: assert(0); break;
+ }
+ return "";
+}
+
char* administration_project_get_status_string(project data)
{
switch(data.state)
@@ -948,27 +965,103 @@ bool administration_is_invoice_valid(invoice* invoice)
return true;
}
+bool administration_update_invoice(invoice* inv)
+{
+ if (!administration_is_invoice_valid(inv)) return false;
+
+ // Invoice is valid but customer id is unset means we need to register a new contact.
+ if (strcmp(inv->customer_id, "") == 0)
+ {
+ contact new_contact = administration_create_empty_contact();
+ strops_copy(inv->customer_id, new_contact.id, sizeof(new_contact.id));
+ strops_copy(inv->customer.id, new_contact.id, sizeof(new_contact.id));
+
+ memcpy(&new_contact, &inv->customer, sizeof(contact));
+ administration_create_contact(new_contact);
+
+ inv->customer = new_contact;
+ }
+
+ // Addressee is same as customer.
+ if (!inv->is_triangulation)
+ {
+ memcpy(&inv->addressee, &inv->customer, sizeof(contact));
+ }
+
+ list_iterator_start(&g_administration.invoices);
+ while (list_iterator_hasnext(&g_administration.invoices)) {
+ invoice* c = (invoice *)list_iterator_next(&g_administration.invoices);
+
+ if (strcmp(c->id, inv->id) == 0)
+ {
+ memcpy(c, inv, sizeof(invoice));
+ list_iterator_stop(&g_administration.invoices);
+ return true;
+ }
+ }
+ list_iterator_stop(&g_administration.invoices);
+ return false;
+}
+
bool administration_add_invoice(invoice* inv)
{
if (!administration_is_invoice_valid(inv)) return false;
// Invoice is valid but customer id is unset means we need to register a new contact.
- if (strcmp(inv->customer_id, ""))
+ if (strcmp(inv->customer_id, "") == 0)
{
contact new_contact = administration_create_empty_contact();
+ strops_copy(inv->customer_id, new_contact.id, sizeof(new_contact.id));
strops_copy(inv->customer.id, new_contact.id, sizeof(new_contact.id));
memcpy(&new_contact, &inv->customer, sizeof(contact));
administration_create_contact(new_contact);
+
+ inv->customer = new_contact;
+ }
+
+ // Addressee is same as customer.
+ if (!inv->is_triangulation)
+ {
+ memcpy(&inv->addressee, &inv->customer, sizeof(contact));
}
invoice* new_inv = (invoice*)malloc(sizeof(invoice));
memcpy((void*)new_inv, (void*)inv, sizeof(invoice));
+ new_inv->status = invoice_status::INVOICE_CONCEPT;
+
list_append(&g_administration.invoices, new_inv);
g_administration.next_id++;
g_administration.next_sequence_number++;
return true;
+}
+
+u32 administration_get_invoice_count()
+{
+ return list_size(&g_administration.invoices);
+}
+
+u32 administration_get_invoices(u32 page_index, u32 page_size, invoice* buffer)
+{
+ assert(buffer);
+
+ u32 write_cursor = 0;
+
+ u32 read_start = page_index * page_size;
+
+ list_iterator_start(&g_administration.invoices);
+ while (list_iterator_hasnext(&g_administration.invoices)) {
+ invoice c = *(invoice *)list_iterator_next(&g_administration.invoices);
+
+ if (g_administration.invoices.iter_pos <= read_start) continue;
+
+ buffer[write_cursor++] = c;
+ if (write_cursor >= page_size) break;
+ }
+ list_iterator_stop(&g_administration.invoices);
+
+ return write_cursor;
} \ No newline at end of file
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 8c0943c..5d89479 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -21,6 +21,12 @@ 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_destroy_invoices()
+{
+ free(cost_center_list_buffer);
+ free(tax_bracket_list_buffer);
+}
+
void ui_setup_invoices()
{
current_view_state = view_state::LIST;
@@ -339,6 +345,8 @@ static void draw_invoice_items_form(invoice* invoice)
ImGui::EndTable();
}
+
+ free(buffer);
}
void draw_invoice_form(invoice* buffer, bool viewing_only = false)
@@ -372,7 +380,7 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
}
ImGui::SameLine();
ImGui::Text("Invoice expires at");
- ImGui::EndDisabled();
+ if (!viewing_only) ImGui::EndDisabled();
// 6. Product/service delivered at
tm delivered_at_date = *gmtime(&buffer->delivered_at);
@@ -390,6 +398,9 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
bool on_autocomplete;
draw_contact_form_ex(&buffer->customer, false, true, &on_autocomplete);
+ // TODO: check if customer info is equal to contact stored in customer id, in case we select from dropdown and edit data after,
+ // this should be handled as a new contact and customer_id should be set to "" so we create a new contact.
+
if (on_autocomplete) {
strops_copy(buffer->customer_id, buffer->customer.id, sizeof(buffer->customer_id));
}
@@ -433,11 +444,16 @@ void draw_invoice_form(invoice* buffer, bool viewing_only = false)
// 13. Invoice items form
draw_invoice_items_form(buffer);
- //if (viewing_only) ImGui::EndDisabled();
+ if (viewing_only) ImGui::EndDisabled();
}
void draw_invoices_list()
{
+ const u32 items_per_page = 50;
+ static s32 current_page = 0;
+ s32 max_page = (administration_get_invoice_count() + items_per_page - 1) / items_per_page;
+ if (max_page == 0) max_page = 1;
+
// Table header controls: create button and pagination.
if (ImGui::Button(localize("form.create")))
{
@@ -446,6 +462,131 @@ void draw_invoices_list()
active_invoice.supplier = administration_get_company_info();
strops_copy(active_invoice.supplier_id, active_invoice.supplier.id, sizeof(active_invoice.supplier_id));
}
+
+ if (current_page >= max_page-1) current_page = max_page-1;
+ if (current_page < 0) current_page = 0;
+
+ // Navigate to prev page button.
+ ImGui::SameLine();
+ bool enable_prev = current_page > 0;
+ if (!enable_prev) ImGui::BeginDisabled();
+ if (ImGui::Button("<< Prev") && current_page > 0) current_page--;
+ if (!enable_prev) ImGui::EndDisabled();
+
+ ImGui::SameLine();
+ ImGui::Text("(%d/%d)", current_page+1, max_page);
+
+ // Navigate to next page button.
+ ImGui::SameLine();
+ bool enable_next = current_page < max_page-1;
+ if (!enable_next) ImGui::BeginDisabled();
+ if (ImGui::Button("Next >>") && current_page < max_page-1) current_page++;
+ if (!enable_next) ImGui::EndDisabled();
+
+ ImGui::Spacing();
+
+ if (ImGui::BeginTable("TableInvoices", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
+
+ ImGui::TableSetupColumn("Invoice Number", ImGuiTableColumnFlags_WidthFixed, 120);
+ ImGui::TableSetupColumn("Customer");
+ ImGui::TableSetupColumn("Addressee");
+ ImGui::TableSetupColumn("Issued At");
+ ImGui::TableSetupColumn("Total");
+ ImGui::TableSetupColumn("Status");
+ ImGui::TableSetupColumn("");
+ ImGui::TableHeadersRow();
+
+ invoice invoice_list[items_per_page];
+ u32 invoice_count = administration_get_invoices(current_page, items_per_page, invoice_list);
+
+ for (u32 i = 0; i < invoice_count; i++) {
+ invoice c = invoice_list[i];
+
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0); ImGui::Text(c.sequential_number);
+ ImGui::TableSetColumnIndex(1); ImGui::Text(c.customer.name);
+ ImGui::TableSetColumnIndex(2); ImGui::Text(c.addressee.name);
+
+ struct tm *lt = localtime(&c.issued_at);
+ char buf[80];
+ strftime(buf, sizeof(buf), "%d-%m-%Y", lt);
+
+ ImGui::TableSetColumnIndex(3); ImGui::Text(buf);
+ ImGui::TableSetColumnIndex(4); ImGui::Text("%.2f %s", c.total, c.currency);
+ ImGui::TableSetColumnIndex(5); ImGui::Text("%s", localize(administration_get_invoice_status_string(&c)));
+ ImGui::TableSetColumnIndex(6);
+
+ char btn_name[20];
+ snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.view"), i);
+ if (ImGui::Button(btn_name)) {
+ active_invoice = c;
+ current_view_state = view_state::VIEW;
+ }
+
+ ImGui::SameLine();
+
+ if (c.status == invoice_status::INVOICE_CONCEPT)
+ {
+ snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.change"), i);
+ if (ImGui::Button(btn_name)) {
+ active_invoice = c;
+ current_view_state = view_state::EDIT;
+ }
+ }
+
+ // ImGui::SameLine();
+ // if (administration_can_contact_be_deleted(c))
+ // {
+ // snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.delete"), i);
+ // if (ImGui::Button(btn_name)) {
+ // selected_for_removal = c;
+ // ImGui::OpenPopup("ConfirmDeletePopup");
+ // }
+ // }
+ }
+
+ // Confirmation popup before contact is deleted definitively.
+ // if (ImGui::BeginPopupModal("ConfirmDeletePopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoTitleBar)) {
+ // ImGui::Text(localize("form.confirmDelete"));
+ // ImGui::Separator();
+
+ // if (ImGui::Button(localize("form.yes"), ImVec2(120, 0))) {
+ // administration_remove_contact(selected_for_removal);
+ // ImGui::CloseCurrentPopup();
+ // }
+ // ImGui::SameLine();
+ // if (ImGui::Button(localize("form.no"), ImVec2(120, 0))) {
+ // ImGui::CloseCurrentPopup();
+ // }
+ // ImGui::EndPopup();
+ // }
+
+ ImGui::EndTable();
+ }
+}
+
+
+static void ui_draw_invoice_update()
+{
+ if (ImGui::Button(localize("form.back"))) {
+ current_view_state = view_state::LIST;
+ }
+
+ draw_invoice_form(&active_invoice);
+
+ bool can_save = administration_is_invoice_valid(&active_invoice);
+ if (!can_save) ImGui::BeginDisabled();
+
+ ImGui::Spacing();
+ if (ImGui::Button(localize("form.save"))) {
+ administration_update_invoice(&active_invoice);
+ current_view_state = view_state::LIST;
+
+ ui_destroy_invoices();
+ ui_setup_invoices();
+ }
+
+ if (!can_save) ImGui::EndDisabled();
}
static void ui_draw_invoice_create()
@@ -463,6 +604,9 @@ static void ui_draw_invoice_create()
if (ImGui::Button(localize("form.save"))) {
administration_add_invoice(&active_invoice);
current_view_state = view_state::LIST;
+
+ ui_destroy_invoices();
+ ui_setup_invoices();
}
if (!can_save) ImGui::EndDisabled();
@@ -474,7 +618,14 @@ void ui_draw_invoices()
{
case view_state::LIST: draw_invoices_list(); break;
case view_state::CREATE: ui_draw_invoice_create(); break;
- case view_state::EDIT: break;
- case view_state::VIEW: break;
+ case view_state::EDIT: ui_draw_invoice_update(); break;
+ case view_state::VIEW:
+ {
+ if (ImGui::Button(localize("form.back"))) {
+ current_view_state = view_state::LIST;
+ }
+
+ draw_invoice_form(&active_invoice, true);
+ } break;
}
} \ No newline at end of file