summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-11-08 14:13:43 +0100
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-11-08 14:13:43 +0100
commit6abbea56a6bfa69cb5e576223301f0c992b646fa (patch)
tree97dabe37ef08202303c9a7a5187de77ac4b286e9
parent887d5f9f8d1309134c72b1f35afc7d007a5c64aa (diff)
activity sidebar for invoices.HEADmaster
-rw-r--r--include/administration.hpp227
-rw-r--r--src/administration.cpp73
-rw-r--r--src/administration_reader.cpp2
-rw-r--r--src/administration_writer.cpp6
-rw-r--r--src/locales/en.cpp7
-rw-r--r--src/ui/ui_expenses.cpp185
-rw-r--r--src/ui/ui_invoices.cpp74
7 files changed, 396 insertions, 178 deletions
diff --git a/include/administration.hpp b/include/administration.hpp
index 09e0cb2..c099318 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -44,6 +44,10 @@
#define MAX_LEN_PROJECT_REPORT_COSTCENTERS 50
#define MAX_BILLING_ITEMS 50
+#define ACTIVITY_MAX_PARAMS 3
+#define ACTIVITY_USER "user"
+#define ACTIVITY_SYSTEM "system"
+
#define MY_COMPANY_ID "C/1"
typedef enum
@@ -398,6 +402,16 @@ typedef struct
typedef struct
{
+ time_t timestamp;
+ char user_name[MAX_LEN_SHORT_DESC];
+ char ref_id[MAX_LEN_ID];
+
+ char message[MAX_LEN_LONG_DESC];
+ char params[ACTIVITY_MAX_PARAMS][MAX_LEN_LONG_DESC];
+} activity;
+
+typedef struct
+{
contact company_info; // Company info used for invoices. User cannot create invoices when this is empty/invalid.
s32 next_id; // Id increment shared across all objects that have an ID.
s32 next_sequence_number; // Sequence number for generating invoice numbers.
@@ -408,9 +422,10 @@ typedef struct
list_t contacts;
list_t projects;
- list_t tax_rates; // Enabled tax rates.
- list_t all_tax_rates; // Tax rates for all countries.
+ list_t tax_rates; // Enabled tax rates.
+ list_t all_tax_rates; // Tax rates for all countries.
list_t cost_centers;
+ list_t activities;
u32 invoice_count;
u32 expense_count;
@@ -449,152 +464,148 @@ namespace administration {
// Setup functions.
// =======================
- void create_empty(char* save_file);
- void create_default(char* save_file);
- void create_from_file(char* save_file);
- void destroy();
+ void create_empty(char* save_file);
+ void create_default(char* save_file);
+ void create_from_file(char* save_file);
+ void destroy();
+ void sort_data();
// Callback functions.
// =======================
- void set_administration_data_changed_event_callback(data_changed_event ev);
- void set_data_deleted_event_callback(data_deleted_event ev);
- void set_invoice_changed_event_callback(invoice_changed_event ev);
- void set_contact_changed_event_callback(contact_changed_event ev);
- void set_taxrate_changed_event_callback(taxrate_changed_event ev);
- void set_costcenter_changed_event_callback(costcenter_changed_event ev);
- void set_project_changed_event_callback(project_changed_event ev);
+ void set_administration_data_changed_event_callback(data_changed_event ev);
+ void set_data_deleted_event_callback(data_deleted_event ev);
+ void set_invoice_changed_event_callback(invoice_changed_event ev);
+ void set_contact_changed_event_callback(contact_changed_event ev);
+ void set_taxrate_changed_event_callback(taxrate_changed_event ev);
+ void set_costcenter_changed_event_callback(costcenter_changed_event ev);
+ void set_project_changed_event_callback(project_changed_event ev);
// Company info functions.
// =======================
- contact company_info_get();
- void company_info_import(contact data);
- void company_info_set(contact data);
+ contact company_info_get();
+ void company_info_import(contact data);
+ void company_info_set(contact data);
// Tax functions.
// =======================
- tax_line* get_tax_line_from_report(tax_report* quarter, char* category);
- void create_tax_statement(tax_statement* statement);
+ tax_line* get_tax_line_from_report(tax_report* quarter, char* category);
+ void create_tax_statement(tax_statement* statement);
// Other functions.
// =======================
- char* get_file_path();
- s32 get_next_id();
- s32 get_next_sequence_number();
- char* get_currency_symbol_for_currency(char* code);
- char* get_default_currency();
- time_t get_default_invoice_expire_duration();
- ai_service get_ai_service();
-
- void set_file_path(char* path);
- void set_next_id(s32 nr);
- void set_next_sequence_number(s32 nr);
- void set_ai_service(ai_service provider);
-
- void create_income_statement(income_statement* statement);
- bool can_create_invoices();
+ char* get_file_path();
+ s32 get_next_id();
+ s32 get_next_sequence_number();
+ char* get_currency_symbol_for_currency(char* code);
+ char* get_default_currency();
+ time_t get_default_invoice_expire_duration();
+ ai_service get_ai_service();
+ void set_file_path(char* path);
+ void set_next_id(s32 nr);
+ void set_next_sequence_number(s32 nr);
+ void set_ai_service(ai_service provider);
+ void create_income_statement(income_statement* statement);
+ bool can_create_invoices();
// Contact functions.
// =======================
- u32 contact_count(contact_filter* filter = 0);
- contact contact_create_empty();
- a_err contact_import(contact data);
- a_err contact_add(contact data);
- a_err contact_update(contact data);
- a_err contact_remove(contact data);
-
- a_err addressee_is_valid(delivery_info data);
- a_err contact_is_valid(contact data);
- bool contact_equals(contact c1, contact c2);
-
- a_err contact_get_by_id(contact* buffer, char* id);
- int contact_get_autocompletions(contact* buffer, int buf_size, char* name);
- u32 contact_get_partial_list(u32 page_index, u32 page_size, contact* buffer, contact_filter* filter = 0);
- u32 contact_get_all(contact* buffer);
+ u32 contact_count(contact_filter* filter = 0);
+ contact contact_create_empty();
+ a_err contact_import(contact data);
+ a_err contact_add(contact data);
+ a_err contact_update(contact data);
+ a_err contact_remove(contact data);
+ a_err addressee_is_valid(delivery_info data);
+ a_err contact_is_valid(contact data);
+ bool contact_equals(contact c1, contact c2);
+ a_err contact_get_by_id(contact* buffer, char* id);
+ int contact_get_autocompletions(contact* buffer, int buf_size, char* name);
+ u32 contact_get_partial_list(u32 page_index, u32 page_size, contact* buffer, contact_filter* filter = 0);
+ u32 contact_get_all(contact* buffer);
// Project functions.
// =======================
- u32 project_count();
- project project_create_empty();
- a_err project_import(project data);
- a_err project_add(project data);
- a_err project_update(project data);
- a_err project_remove(project data);
- void project_cancel(project data);
-
- a_err project_is_valid(project data);
-
- char* project_get_status_string(project data);
- u32 project_get_all(project* buffer);
- u32 project_get_partial_list(u32 page_index, u32 page_size, project* buffer);
- a_err project_get_by_id(project* buffer, char* id);
+ u32 project_count();
+ project project_create_empty();
+ a_err project_import(project data);
+ a_err project_add(project data);
+ a_err project_update(project data);
+ a_err project_remove(project data);
+ void project_cancel(project data);
+ a_err project_is_valid(project data);
+ char* project_get_status_string(project data);
+ u32 project_get_all(project* buffer);
+ u32 project_get_partial_list(u32 page_index, u32 page_size, project* buffer);
+ a_err project_get_by_id(project* buffer, char* id);
// Tax rate functions.
// =======================
- u32 tax_rate_count();
- tax_rate tax_rate_create_empty();
- a_err tax_rate_import(tax_rate data);
- a_err tax_rate_enable(tax_rate data);
- a_err tax_rate_is_enabled(tax_rate data);
- a_err tax_rate_disable(tax_rate data);
+ u32 tax_rate_count();
+ tax_rate tax_rate_create_empty();
+ a_err tax_rate_import(tax_rate data);
+ a_err tax_rate_enable(tax_rate data);
+ a_err tax_rate_is_enabled(tax_rate data);
+ a_err tax_rate_disable(tax_rate data);
/// @brief Find tax rate by internal code, across all available tax rates.
- a_err tax_rate_get_by_internal_code(tax_rate* buffer, char* id);
+ a_err tax_rate_get_by_internal_code(tax_rate* buffer, char* id);
/// @brief Get all enabled tax rates.
/// @return tax rate count
- u32 tax_rate_get_all(tax_rate* buffer, tax_rate_type type);
+ u32 tax_rate_get_all(tax_rate* buffer, tax_rate_type type);
// Cost center functions.
// =======================
- u32 cost_center_count();
+ u32 cost_center_count();
cost_center cost_center_create_empty();
- a_err cost_center_import(cost_center data);
- a_err cost_center_add(cost_center data);
- a_err cost_center_update(cost_center data);
-
- a_err cost_center_is_valid(cost_center data);
-
- u32 cost_center_get_all(cost_center* buffer);
- a_err cost_center_get_by_id(cost_center* buffer, char* id);
+ a_err cost_center_import(cost_center data);
+ a_err cost_center_add(cost_center data);
+ a_err cost_center_update(cost_center data);
+ a_err cost_center_is_valid(cost_center data);
+ u32 cost_center_get_all(cost_center* buffer);
+ a_err cost_center_get_by_id(cost_center* buffer, char* id);
// Invoice functions.
// =======================
- u32 invoice_count();
- u32 invoice_get_incomming_count();
- u32 invoice_get_outgoing_count();
- invoice invoice_create_empty();
- a_err invoice_import(invoice* invoice);
- a_err invoice_add(invoice* invoice);
- a_err invoice_update(invoice* invoice);
- a_err invoice_remove(invoice* invoice);
- void invoice_set_currency(invoice* invoice, char* currency);
- invoice invoice_create_copy(invoice* invoice);
- void invoice_destroy(invoice* invoice);
-
- a_err invoice_is_valid(invoice* invoice);
- bool invoice_has_intra_community_services(invoice* invoice);
-
- char* invoice_get_status_string(invoice* invoice);
- u32 invoice_get_partial_list_outgoing(u32 page_index, u32 page_size, invoice* buffer);
- u32 invoice_get_partial_list_incomming(u32 page_index, u32 page_size, invoice* buffer);
- u32 invoice_get_all(invoice* buffer);
- a_err invoice_get_by_id(invoice* buffer, char* id);
- u32 invoice_get_tax_rates(invoice* invoice, tax_rate* buffer);
- bool invoice_get_subtotal_for_tax_rate(invoice* invoice, tax_rate rate, tax_subtotal* buffer);
+ u32 invoice_count();
+ u32 invoice_get_incomming_count();
+ u32 invoice_get_outgoing_count();
+ invoice invoice_create_empty();
+ a_err invoice_import(invoice* invoice);
+ a_err invoice_add(invoice* invoice);
+ a_err invoice_update(invoice* invoice);
+ a_err invoice_remove(invoice* invoice);
+ void invoice_set_currency(invoice* invoice, char* currency);
+ invoice invoice_create_copy(invoice* invoice);
+ void invoice_destroy(invoice* invoice);
+ a_err invoice_is_valid(invoice* invoice);
+ bool invoice_has_intra_community_services(invoice* invoice);
+ char* invoice_get_status_string(invoice* invoice);
+ u32 invoice_get_partial_list_outgoing(u32 page_index, u32 page_size, invoice* buffer);
+ u32 invoice_get_partial_list_incomming(u32 page_index, u32 page_size, invoice* buffer);
+ u32 invoice_get_all(invoice* buffer);
+ a_err invoice_get_by_id(invoice* buffer, char* id);
+ u32 invoice_get_tax_rates(invoice* invoice, tax_rate* buffer);
+ bool invoice_get_subtotal_for_tax_rate(invoice* invoice, tax_rate rate, tax_subtotal* buffer);
// Billing item functions.
// =======================
- u32 billing_item_count(invoice* invoice);
+ u32 billing_item_count(invoice* invoice);
billing_item billing_item_create_empty();
- a_err billing_item_import_to_invoice(invoice* invoice, billing_item item);
- a_err billing_item_add_to_invoice(invoice* invoice, billing_item item);
- a_err billing_item_update_in_invoice(invoice* invoice, billing_item item);
- a_err billing_item_remove_from_invoice(invoice* invoice, billing_item item);
+ a_err billing_item_import_to_invoice(invoice* invoice, billing_item item);
+ a_err billing_item_add_to_invoice(invoice* invoice, billing_item item);
+ a_err billing_item_update_in_invoice(invoice* invoice, billing_item item);
+ a_err billing_item_remove_from_invoice(invoice* invoice, billing_item item);
tax_subtotal billing_item_convert_to_default_currency(invoice* invoice, billing_item item);
- a_err billing_item_is_valid(billing_item item);
+ a_err billing_item_is_valid(billing_item item);
+
+ u32 billing_item_get_all_for_invoice(invoice* invoice, billing_item* buffer);
- u32 billing_item_get_all_for_invoice(invoice* invoice, billing_item* buffer);
+ // Activity functions.
+ // ===================
+ a_err activity_add(char* user, char* ref_id, char* message, ...);
+ u32 activity_get_all_for_object(activity* buffer, char* ref_id);
} \ No newline at end of file
diff --git a/src/administration.cpp b/src/administration.cpp
index cb158e2..d7e4eed 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -129,6 +129,7 @@ void administration_create()
list_init(&g_administration.contacts);
list_init(&g_administration.projects);
list_init(&g_administration.tax_rates);
+ list_init(&g_administration.activities);
list_init(&g_administration.all_tax_rates);
list_init(&g_administration.cost_centers);
strops::copy(g_administration.path, "", sizeof(g_administration.path));
@@ -176,6 +177,18 @@ static void administration_destroy_invoices()
list_iterator_stop(&g_administration.invoices);
}
+int sort_invoice_time_t(const void *a, const void *b) {
+ invoice* inv1 = (invoice*)a;
+ invoice* inv2 = (invoice*)b;
+ return (int)(inv1->issued_at - inv2->issued_at);
+}
+
+void administration::sort_data()
+{
+ list_attributes_comparator(&g_administration.invoices, sort_invoice_time_t);
+ list_sort(&g_administration.invoices, 1);
+}
+
void administration::destroy()
{
is_initialized = false;
@@ -183,6 +196,7 @@ void administration::destroy()
administration_destroy_list(&g_administration.invoices);
administration_destroy_list(&g_administration.contacts);
administration_destroy_list(&g_administration.projects);
+ administration_destroy_list(&g_administration.activities);
administration_destroy_list(&g_administration.tax_rates);
administration_destroy_list(&g_administration.all_tax_rates);
administration_destroy_list(&g_administration.cost_centers);
@@ -353,8 +367,8 @@ void administration::create_tax_statement(tax_statement* statement)
time_t youngest = 0;
for (u32 i = 0; i < invoice_count; i++)
{
- if (invoice_buffer[i].delivered_at < oldest) oldest = invoice_buffer[i].delivered_at;
- if (invoice_buffer[i].delivered_at > youngest) youngest = invoice_buffer[i].delivered_at;
+ if (invoice_buffer[i].delivered_at < oldest) oldest = country::get_invoice_date_to_use_for_tax_report(country_code, &invoice_buffer[i]);
+ if (invoice_buffer[i].delivered_at > youngest) youngest = country::get_invoice_date_to_use_for_tax_report(country_code, &invoice_buffer[i]);
}
u16 oldest_year;
@@ -527,7 +541,7 @@ void administration::create_income_statement(income_statement* statement)
u16 yy;
u8 qq;
- time_t_to_quarter(inv->issued_at, &yy, &qq);
+ time_t_to_quarter(inv->delivered_at, &yy, &qq);
u32 report_index = (qq + (yy*4)) - (oldest_quarter + (oldest_year*4));
@@ -1494,6 +1508,7 @@ a_err administration::invoice_update(invoice* inv)
if (invoice_changed_event_callback) invoice_changed_event_callback(c);
+ administration::activity_add(ACTIVITY_USER, c->id, "activity.update_invoice", 0);
return A_ERR_SUCCESS;
}
}
@@ -1547,9 +1562,11 @@ a_err administration::invoice_add(invoice* inv)
memops::copy(new_inv, &copy, sizeof(invoice));
- if (!list_append(&g_administration.invoices, new_inv)) {
+ if (!list_prepend(&g_administration.invoices, new_inv)) {
return A_ERR_GENERIC;
- }
+ }
+
+ administration::activity_add(ACTIVITY_USER, inv->id, "activity.add_invoice", 0);
g_administration.next_id++;
g_administration.next_sequence_number++;
@@ -1900,4 +1917,50 @@ billing_item administration::billing_item_create_empty()
memops::zero(&item, sizeof(billing_item));
item.amount = 1;
return item;
+}
+
+// Activity functions.
+// ===================
+a_err administration::activity_add(char* user, char* ref_id, char* message, ...)
+{
+ activity* new_activity = (activity*)memops::alloc(sizeof(activity));
+ strops::copy(new_activity->user_name, user, MAX_LEN_SHORT_DESC);
+ strops::copy(new_activity->ref_id, ref_id, MAX_LEN_ID);
+ strops::copy(new_activity->message, message, MAX_LEN_LONG_DESC);
+ new_activity->timestamp = time(NULL);
+
+ va_list args;
+ va_start(args, message);
+
+ char* param;
+ u32 param_count = 0;
+ do
+ {
+ param = va_arg(args, char*);
+ if (param != 0) {
+ strops::copy(new_activity->params[param_count++], param, MAX_LEN_LONG_DESC);
+ }
+ } while (param && param_count < ACTIVITY_MAX_PARAMS);
+
+ va_end(args);
+
+ if (!list_prepend(&g_administration.activities, new_activity)) {
+ return A_ERR_GENERIC;
+ }
+
+ return A_ERR_SUCCESS;
+}
+
+u32 administration::activity_get_all_for_object(activity* buffer, char* ref_id)
+{
+ u32 write_cursor = 0;
+
+ list_iterator_start(&g_administration.activities);
+ while (list_iterator_hasnext(&g_administration.activities)) {
+ activity c = *(activity *)list_iterator_next(&g_administration.activities);
+ if (strops::equals(c.ref_id, ref_id)) buffer[write_cursor++] = c;
+ }
+ list_iterator_stop(&g_administration.activities);
+
+ return write_cursor;
} \ No newline at end of file
diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp
index 05133f8..34cfe8f 100644
--- a/src/administration_reader.cpp
+++ b/src/administration_reader.cpp
@@ -101,6 +101,8 @@ bool administration_reader::open_existing(char* file_path)
zip_close(zip);
+ administration::sort_data();
+
logger::info("Imported '%s' in %.3fms.", file_path, STOPWATCH_TIME);
return true;
diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp
index c699a0a..f12cf1f 100644
--- a/src/administration_writer.cpp
+++ b/src/administration_writer.cpp
@@ -357,7 +357,8 @@ static char* get_eas_id_for_contact(contact contact)
if (strops::equals(country_code, "IT")) return contact.businessid; // Italy
if (strops::equals(country_code, "FI")) return contact.businessid; // Finland
if (strops::equals(country_code, "DK")) return contact.businessid; // Denmark
- return NULL; // Unknown country code
+
+ return contact.businessid; // Unknown country code
}
static char* get_eas_scheme_for_contact(contact contact)
@@ -397,7 +398,8 @@ static char* get_eas_scheme_for_contact(contact contact)
if (strops::equals(country_code, "SI")) return "9949"; // Slovenia
if (strops::equals(country_code, "SK")) return "9950"; // Slovakia
if (strops::equals(country_code, "ES")) return "9920"; // Spain
- return NULL; // Unknown country code
+
+ return "0203"; // Hack
}
bool isEmptyTag(const char *start, const char *end) {
diff --git a/src/locales/en.cpp b/src/locales/en.cpp
index d949609..752e328 100644
--- a/src/locales/en.cpp
+++ b/src/locales/en.cpp
@@ -54,6 +54,7 @@ locale_entry en_locales[] = {
{"form.create", "+ Create"},
{"form.back", "Back"},
{"form.save", "Save"},
+ {"form.cancel", "Cancel"},
{"form.yes", "Yes"},
{"form.no", "No"},
{"form.change", "Change"},
@@ -721,7 +722,7 @@ locale_entry en_locales[] = {
{"taxrate.code.NL_IN/IEUV/21","Intra-EU reverse charge 21% VAT"},
{"taxrate.code.NL_IN/IEUV/9","Intra-EU reverse charge 9% VAT"},
- // Tax statement strings.
+ // NL Tax statement strings.
{"taxes.total", "Total"},
{"taxes.nl.1", "Domestic supplies and services"},
@@ -746,6 +747,10 @@ locale_entry en_locales[] = {
{"taxes.nl.5", "Input tax and small businesses scheme (KOR)"},
{"taxes.nl.5a", "VAT due (sections 1 to 4)"},
{"taxes.nl.5b", "Input tax (VAT deductible)"},
+
+ // Activity strings.
+ {"activity.update_invoice", "updated invoice"},
+ {"activity.add_invoice", "created invoice"},
};
int en_locale_count = sizeof(en_locales) / sizeof(en_locales[0]); \ No newline at end of file
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index 5556cd6..3ea31e1 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -26,19 +26,34 @@
#include "administration.hpp"
#include "administration_writer.hpp"
+static activity* activity_buffer = 0;
+static u32 activity_count;
+
static importer::invoice_request* active_import_request = 0;
static ui::view_state current_view_state = ui::view_state::LIST_ALL;
static invoice active_invoice = {0};
static invoice selected_for_removal = {0};
-
+static const float sidepanel_width = 200.0f;
static billing_item* invoice_items_buffer = 0;
void draw_invoice_items_form(invoice* invoice, bool outgoing = true);
+static void _reload_activities()
+{
+ activity_count = administration::activity_get_all_for_object(activity_buffer, active_invoice.id);
+}
+
+static void _set_active_invoice(invoice inv)
+{
+ active_invoice = inv;
+ _reload_activities();
+}
+
void ui::destroy_expenses()
{
memops::unalloc(invoice_items_buffer);
+ memops::unalloc(activity_buffer);
}
void ui::setup_expenses()
@@ -50,7 +65,8 @@ void ui::setup_expenses()
current_view_state = ui::view_state::LIST_ALL;
}
- active_invoice = administration::invoice_create_empty();
+ activity_buffer = (activity*)memops::alloc(sizeof(activity) * 250);
+ _set_active_invoice(administration::invoice_create_empty());
u32 invoice_items_count = MAX_BILLING_ITEMS;
invoice_items_buffer = (billing_item*)memops::alloc(sizeof(billing_item) * invoice_items_count);
@@ -215,22 +231,35 @@ static void draw_expenses_list()
ImGui::Spacing();
- if (ImGui::BeginTable("TableInvoices", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
+ if (ImGui::BeginTable("TableInvoices", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn(locale::get("invoice.table.invoicenumber"), ImGuiTableColumnFlags_WidthFixed, 120);
ImGui::TableSetupColumn(locale::get("invoice.table.sender"));
ImGui::TableSetupColumn(locale::get("invoice.table.customer"));
- ImGui::TableSetupColumn(locale::get("invoice.table.issuedat"));
- ImGui::TableSetupColumn(locale::get("invoice.table.total"));
- ImGui::TableSetupColumn(locale::get("invoice.table.status"));
- ImGui::TableSetupColumn("");
+ ImGui::TableSetupColumn(locale::get("invoice.table.issuedat"), ImGuiTableColumnFlags_WidthFixed, 90);
+ ImGui::TableSetupColumn(locale::get("invoice.table.total"), ImGuiTableColumnFlags_WidthFixed, 90);
+ ImGui::TableSetupColumn(locale::get("invoice.table.status"), ImGuiTableColumnFlags_WidthFixed, 90);
ImGui::TableHeadersRow();
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(0);
+
+ ImGui::PushID(i);
+ ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap;
+ bool selected = false;
+ if (ImGui::Selectable("##invisible_selectable", selected, selectable_flags, ImVec2(0, ImGui::GetFrameHeight()-4.0f)))
+ {
+ _set_active_invoice(c);
+ current_view_state = ui::view_state::VIEW_EXISTING;
+ }
+ ImGui::SetItemAllowOverlap();
+ ImGui::SameLine();
+ ImGui::PopID();
+
+ ImGui::Text(c.sequential_number);
if (administration::invoice_is_valid(&c) != A_ERR_SUCCESS)
{
@@ -249,8 +278,8 @@ static void draw_expenses_list()
ImGui::TableSetColumnIndex(3); ImGui::Text(buf);
ImGui::TableSetColumnIndex(4); ImGui::Text("%.2f %s", c.total, c.currency);
ImGui::TableSetColumnIndex(5); ImGui::Text("%s", locale::get(administration::invoice_get_status_string(&c)));
- ImGui::TableSetColumnIndex(6);
+ /*
char btn_name[20];
strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
if (ImGui::Button(btn_name)) {
@@ -271,23 +300,7 @@ static void draw_expenses_list()
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(locale::get("form.confirmDelete"));
- ImGui::Separator();
-
- if (ImGui::Button(locale::get("form.yes"), ImVec2(120, 0))) {
- administration::invoice_remove(&selected_for_removal);
- ImGui::CloseCurrentPopup();
- }
- ImGui::SameLine();
- if (ImGui::Button(locale::get("form.no"), ImVec2(120, 0))) {
- ImGui::CloseCurrentPopup();
- }
- ImGui::EndPopup();
+ }*/
}
ImGui::EndTable();
@@ -299,26 +312,75 @@ static void _reset_to_default_view()
current_view_state = ui::view_state::LIST_ALL;
ui::destroy_expenses();
ui::setup_expenses();
+ activity_count = 0;
+}
+
+static void _reset_to_invoice_view()
+{
+ current_view_state = ui::view_state::VIEW_EXISTING;
+}
+
+static void _draw_activity_sidepanel()
+{
+ ImGui::SameLine();
+ ImGui::SeparatorEx(1 << 1, 3.0f);
+ ImGui::SameLine();
+
+ ImGui::BeginChild("##historyPanel", ImVec2(sidepanel_width, 0), ImGuiChildFlags_None);
+ {
+ for (u32 i = 0; i < activity_count; i++)
+ {
+ activity ac = activity_buffer[i];
+
+ char time_buffer[26];
+ struct tm* tm_info;
+ tm_info = localtime(&ac.timestamp);
+ strftime(time_buffer, 26, "%d/%m/%Y %H:%M", tm_info);
+
+ ImGui::Text(time_buffer);
+
+ ImGui::PushFont(ui::fontBold);
+ ImGui::Text(ac.user_name);
+ ImGui::PopFont();
+ ImGui::SameLine();
+
+ ImGui::TextWrapped("%s", locale::get(ac.message));
+ ImGui::Spacing();
+ }
+ }
+
+ ImGui::EndChild();
}
static void draw_expense_update()
{
- if (ImGui::Button(locale::get("form.back"), true, false)) {
- _reset_to_default_view();
+ if (ImGui::Button(locale::get("form.cancel"), true, false)) {
+ current_view_state = ui::view_state::VIEW_EXISTING;
}
-
- draw_expense_form(&active_invoice);
- bool can_save = administration::invoice_is_valid(&active_invoice) == A_ERR_SUCCESS;
- if (!can_save) ImGui::BeginDisabled();
-
+ { // Save button
+ bool can_save = administration::invoice_is_valid(&active_invoice) == A_ERR_SUCCESS;
+ if (!can_save) ImGui::BeginDisabled();
+ ImGui::SameLine();
+ if (ImGui::Button(locale::get("form.save"), true)) {
+ administration_writer::set_write_completed_event_callback(_reset_to_invoice_view);
+ administration::invoice_update(&active_invoice);
+
+ _reload_activities();
+ }
+ if (!can_save) ImGui::EndDisabled();
+ }
+
+ ImGui::Spacing();
+ ImGui::Separator(3.0f);
ImGui::Spacing();
- if (ImGui::Button(locale::get("form.save"), true)) {
- administration_writer::set_write_completed_event_callback(_reset_to_default_view);
- administration::invoice_update(&active_invoice);
- }
+
+ float availableWidth = ImGui::GetContentRegionAvail().x;
+ ImGui::BeginChild("##invoicePanel", ImVec2(availableWidth - sidepanel_width, 0), ImGuiChildFlags_None);
+ draw_expense_form(&active_invoice);
+ ImGui::EndChild();
- if (!can_save) ImGui::EndDisabled();
+ _draw_activity_sidepanel();
}
static void draw_expense_create()
@@ -326,19 +388,28 @@ static void draw_expense_create()
if (ImGui::Button(locale::get("form.back"), true, false)) {
_reset_to_default_view();
}
-
- draw_expense_form(&active_invoice);
- bool can_save = administration::invoice_is_valid(&active_invoice) == A_ERR_SUCCESS;
- if (!can_save) ImGui::BeginDisabled();
-
- ImGui::Spacing();
- if (ImGui::Button(locale::get("form.save"), true)) {
- administration_writer::set_write_completed_event_callback(_reset_to_default_view);
- administration::invoice_add(&active_invoice);
- }
+ { // Save button
+ bool can_save = administration::invoice_is_valid(&active_invoice) == A_ERR_SUCCESS;
+ if (!can_save) ImGui::BeginDisabled();
+ ImGui::SameLine();
+ if (ImGui::Button(locale::get("form.save"), true)) {
+ administration_writer::set_write_completed_event_callback(_reset_to_invoice_view);
+ administration::invoice_add(&active_invoice);
+
+ _reload_activities();
+ }
+ if (!can_save) ImGui::EndDisabled();
+ }
- if (!can_save) ImGui::EndDisabled();
+ ImGui::Spacing();
+ ImGui::Separator(3.0f);
+ ImGui::Spacing();
+
+ float availableWidth = ImGui::GetContentRegionAvail().x;
+ ImGui::BeginChild("##invoicePanel", ImVec2(availableWidth - sidepanel_width, 0), ImGuiChildFlags_None);
+ draw_expense_form(&active_invoice);
+ ImGui::EndChild();
}
static void draw_expense_view()
@@ -347,7 +418,23 @@ static void draw_expense_view()
_reset_to_default_view();
}
+ ImGui::SameLine();
+
+ if (ImGui::Button(locale::get("form.change"))) {
+ _set_active_invoice(administration::invoice_create_copy(&active_invoice)); // We create a copy because of billing item list pointers.
+ current_view_state = ui::view_state::EDIT_EXISTING;
+ }
+
+ ImGui::Spacing();
+ ImGui::Separator(3.0f);
+ ImGui::Spacing();
+
+ float availableWidth = ImGui::GetContentRegionAvail().x;
+ ImGui::BeginChild("##invoicePanel", ImVec2(availableWidth - sidepanel_width, 0), ImGuiChildFlags_None);
draw_expense_form(&active_invoice, true);
+ ImGui::EndChild();
+
+ _draw_activity_sidepanel();
}
static void draw_import_request()
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 0e81259..ff3c73b 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -25,23 +25,38 @@
#include "administration.hpp"
#include "administration_writer.hpp"
+static activity* activity_buffer = 0;
+static u32 activity_count;
static ui::view_state current_view_state = ui::view_state::LIST_ALL;
static invoice active_invoice = {0};
static invoice selected_for_removal = {0};
static const float sidepanel_width = 200.0f;
-
static billing_item* invoice_items_buffer = 0;
+static void _reload_activities()
+{
+ activity_count = administration::activity_get_all_for_object(activity_buffer, active_invoice.id);
+}
+
+static void _set_active_invoice(invoice inv)
+{
+ active_invoice = inv;
+ _reload_activities();
+}
+
void ui::destroy_invoices()
{
memops::unalloc(invoice_items_buffer);
+ memops::unalloc(activity_buffer);
}
void ui::setup_invoices()
{
+ activity_buffer = (activity*)memops::alloc(sizeof(activity) * 250);
+
current_view_state = ui::view_state::LIST_ALL;
- active_invoice = administration::invoice_create_empty();
+ _set_active_invoice(administration::invoice_create_empty());
u32 invoice_items_count = MAX_BILLING_ITEMS;
invoice_items_buffer = (billing_item*)memops::alloc(sizeof(billing_item) * invoice_items_count);
@@ -279,7 +294,7 @@ static void draw_invoices_list()
if (ImGui::Button(locale::get("form.create")))
{
current_view_state = ui::view_state::CREATE;
- active_invoice = administration::invoice_create_empty(); // @leak
+ _set_active_invoice(administration::invoice_create_empty());
active_invoice.supplier = administration::company_info_get();
active_invoice.is_outgoing = 1;
active_invoice.status = invoice_status::INVOICE_CONCEPT;
@@ -328,7 +343,7 @@ static void draw_invoices_list()
bool selected = false;
if (ImGui::Selectable("##invisible_selectable", selected, selectable_flags, ImVec2(0, ImGui::GetFrameHeight()-4.0f)))
{
- active_invoice = c;
+ _set_active_invoice(c);
current_view_state = ui::view_state::VIEW_EXISTING;
}
ImGui::SetItemAllowOverlap();
@@ -372,11 +387,44 @@ static void _reset_to_default_view()
current_view_state = ui::view_state::LIST_ALL;
ui::destroy_invoices();
ui::setup_invoices();
+ activity_count = 0;
+}
+
+static void _draw_activity_sidepanel()
+{
+ ImGui::SameLine();
+ ImGui::SeparatorEx(1 << 1, 3.0f);
+ ImGui::SameLine();
+
+ ImGui::BeginChild("##historyPanel", ImVec2(sidepanel_width, 0), ImGuiChildFlags_None);
+ {
+ for (u32 i = 0; i < activity_count; i++)
+ {
+ activity ac = activity_buffer[i];
+
+ char time_buffer[26];
+ struct tm* tm_info;
+ tm_info = localtime(&ac.timestamp);
+ strftime(time_buffer, 26, "%d/%m/%Y %H:%M", tm_info);
+
+ ImGui::Text(time_buffer);
+
+ ImGui::PushFont(ui::fontBold);
+ ImGui::Text(ac.user_name);
+ ImGui::PopFont();
+ ImGui::SameLine();
+
+ ImGui::TextWrapped("%s", locale::get(ac.message));
+ ImGui::Spacing();
+ }
+ }
+
+ ImGui::EndChild();
}
static void draw_invoice_update()
{
- if (ImGui::Button(locale::get("form.back"), true, false)) {
+ if (ImGui::Button(locale::get("form.cancel"), true, false)) {
current_view_state = ui::view_state::VIEW_EXISTING;
}
@@ -387,6 +435,8 @@ static void draw_invoice_update()
if (ImGui::Button(locale::get("form.save"), true)) {
administration_writer::set_write_completed_event_callback(_reset_to_invoice_view);
administration::invoice_update(&active_invoice);
+
+ _reload_activities();
}
if (!can_save) ImGui::EndDisabled();
}
@@ -399,6 +449,8 @@ static void draw_invoice_update()
ImGui::BeginChild("##invoicePanel", ImVec2(availableWidth - sidepanel_width, 0), ImGuiChildFlags_None);
draw_invoice_form(&active_invoice);
ImGui::EndChild();
+
+ _draw_activity_sidepanel();
}
static void draw_invoice_create()
@@ -414,6 +466,8 @@ static void draw_invoice_create()
if (ImGui::Button(locale::get("form.save"), true)) {
administration_writer::set_write_completed_event_callback(_reset_to_invoice_view);
administration::invoice_add(&active_invoice);
+
+ _reload_activities();
}
if (!can_save) ImGui::EndDisabled();
}
@@ -437,7 +491,7 @@ static void draw_invoice_view()
ImGui::SameLine();
if (ImGui::Button(locale::get("form.change"))) {
- active_invoice = administration::invoice_create_copy(&active_invoice); // We create a copy because of billing item list pointers.
+ _set_active_invoice(administration::invoice_create_copy(&active_invoice)); // We create a copy because of billing item list pointers.
current_view_state = ui::view_state::EDIT_EXISTING;
}
@@ -480,13 +534,7 @@ static void draw_invoice_view()
draw_invoice_form(&active_invoice, true);
ImGui::EndChild();
- ImGui::SameLine();
- ImGui::SeparatorEx(1 << 1, 3.0f);
- ImGui::SameLine();
-
- ImGui::BeginChild("##historyPanel", ImVec2(sidepanel_width, 0), ImGuiChildFlags_None);
- // Sidepanel
- ImGui::EndChild();
+ _draw_activity_sidepanel();
}
void ui::draw_invoices()