diff options
| author | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-04 12:36:52 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-04 12:36:52 +0100 |
| commit | 9a3cfd077013e42d4b3f788b78f45edf3b46ef07 (patch) | |
| tree | 96f36ba34e47ef3d757d341f7f30d1492eca70e0 | |
| parent | 66a918a4621f1ecb828e68eac94fdb34852e9570 (diff) | |
activity list write
| -rw-r--r-- | TODO | 2 | ||||
| -rw-r--r-- | include/administration.hpp | 7 | ||||
| -rw-r--r-- | include/administration_writer.hpp | 11 | ||||
| -rw-r--r-- | include/file_templates.hpp | 8 | ||||
| -rw-r--r-- | src/administration.cpp | 37 | ||||
| -rw-r--r-- | src/administration_reader.cpp | 2 | ||||
| -rw-r--r-- | src/administration_writer.cpp | 53 | ||||
| -rw-r--r-- | src/main_linux.cpp | 8 | ||||
| -rw-r--r-- | src/ui/ui_invoices.cpp | 4 |
9 files changed, 91 insertions, 41 deletions
@@ -12,9 +12,9 @@ Testing: - write tests that check error handling for corrupt files. (e.g. references to tax rates, project and cost center that failed to load) Features: +- save api keys for all options individually so user can switch without losing key - Refactor contact and project UI to be like invoice & expenses - Timeline for invoice modifications (e.g. edited, status changed, paid) -- When creating a new openbooks file, user should only be able to set company info before anything else. - Show AI balance available in settings page - Handle invalid api key response from AI backends and display in settings UI - error log for tax report to display invoices not yet supported for tax generation or invoices with invalid tax rates diff --git a/include/administration.hpp b/include/administration.hpp index d899987..1fac81c 100644 --- a/include/administration.hpp +++ b/include/administration.hpp @@ -424,9 +424,7 @@ 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 @@ -627,7 +625,8 @@ namespace administration { // Activity functions. // =================== - a_err activity_add(char* user, char* ref_id, char* message, ...); + a_err activity_add(char* user, char* ref_id, char* message); u32 activity_get_all_for_object(activity* buffer, char* ref_id); - + u32 activity_count(); + a_err activity_get_by_index(u32 index, activity* buffer); }
\ No newline at end of file diff --git a/include/administration_writer.hpp b/include/administration_writer.hpp index 488330b..81630a7 100644 --- a/include/administration_writer.hpp +++ b/include/administration_writer.hpp @@ -18,7 +18,8 @@ #include "administration.hpp" -#define ADMIN_FILE_INFO "info.xml" +#define ACTIVITY_FILE "activities.xml" +#define ADMIN_INFO_FILE "info.xml" typedef void (*write_completed_event)(); @@ -33,20 +34,14 @@ namespace administration_writer { bool create(); void destroy(); - bool start_new(); - - // Individual entity writing. bool delete_entry(const char* id); bool save_project_blocking(project project); bool save_cost_center_blocking(cost_center cost); bool save_tax_rate_blocking(tax_rate rate); bool save_contact_blocking(contact c); bool save_invoice_blocking(invoice inv); - - // Archiving functions. bool save_administration_info_blocking(); bool save_all_tax_rates_blocking(); bool save_all_cost_centers_blocking(); - bool save_all_async(); - + bool save_activities_blocking(); }
\ No newline at end of file diff --git a/include/file_templates.hpp b/include/file_templates.hpp index 5404270..ec78cb7 100644 --- a/include/file_templates.hpp +++ b/include/file_templates.hpp @@ -130,6 +130,14 @@ namespace file_template { "<InvoiceExtras>\n" " <Status>{{INVOICE_STATUS}}</Status>\n" "</InvoiceExtras>\n"; + + static const char *activity_template = + "<Activity>\n" + " <Timestamp>{{TIMESTAMP}}</Timestamp>\n" + " <User>{{USERNAME}}</User>\n" + " <RefId>{{REF_ID}}</RefId>\n" + " <Message>{{MESSAGE}}</Message>\n" + "</Activity>\n"; static const char *peppol_invoice_template = /*"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"*/ diff --git a/src/administration.cpp b/src/administration.cpp index 44438ff..0b1a0db 100644 --- a/src/administration.cpp +++ b/src/administration.cpp @@ -1520,7 +1520,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); + administration::activity_add(ACTIVITY_USER, c->id, "activity.update_invoice"); return A_ERR_SUCCESS; } } @@ -1578,7 +1578,7 @@ a_err administration::invoice_add(invoice* inv) return A_ERR_GENERIC; } - administration::activity_add(ACTIVITY_USER, inv->id, "activity.add_invoice", 0); + administration::activity_add(ACTIVITY_USER, inv->id, "activity.add_invoice"); g_administration.next_id++; g_administration.next_sequence_number++; @@ -1933,7 +1933,7 @@ billing_item administration::billing_item_create_empty() // Activity functions. // =================== -a_err administration::activity_add(char* user, char* ref_id, char* message, ...) +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); @@ -1941,21 +1941,6 @@ a_err administration::activity_add(char* user, char* ref_id, char* message, ...) 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; } @@ -1975,4 +1960,20 @@ u32 administration::activity_get_all_for_object(activity* buffer, char* ref_id) list_iterator_stop(&g_administration.activities); return write_cursor; +} + +u32 administration::activity_count() +{ + return list_size(&g_administration.activities); +} + +a_err administration::activity_get_by_index(u32 index, activity* buffer) +{ + assert(buffer); + + activity* c = (activity *)list_get_at(&g_administration.activities, index); + if (c == NULL) return A_ERR_NOT_FOUND; + *buffer = *c; + + return A_ERR_SUCCESS; }
\ No newline at end of file diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp index 3706e72..6c7234d 100644 --- a/src/administration_reader.cpp +++ b/src/administration_reader.cpp @@ -70,7 +70,7 @@ bool administration_reader::open_existing(char* file_path) continue; } - if (strops::equals(name, ADMIN_FILE_INFO)) + if (strops::equals(name, ADMIN_INFO_FILE)) { administration_reader::import_administration_info(buffer, (size_t)size); } diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp index 3191be7..8cd1dcf 100644 --- a/src/administration_writer.cpp +++ b/src/administration_writer.cpp @@ -933,7 +933,7 @@ bool administration_writer::save_administration_info_blocking() //// Write to Disk. int final_length = (int)strops::length(file_content); if (!xml_string_is_valid((uint8_t*)file_content, final_length)) result = 0; - else if (!write_to_zip(ADMIN_FILE_INFO, file_content, final_length)) result = 0; + else if (!write_to_zip(ADMIN_INFO_FILE, file_content, final_length)) result = 0; memops::unalloc(file_content); @@ -941,4 +941,55 @@ bool administration_writer::save_administration_info_blocking() else logger::error("Failed to save administration info."); return result; +} + +bool administration_writer::save_activities_blocking() +{ + STOPWATCH_START; + + u32 activity_count = administration::activity_count(); + u32 activity_list_buffer_size = (u32)strops::length(file_template::activity_template) * 3 * activity_count; // Ballpark list size. // @TODO + if (activity_list_buffer_size == 0) activity_list_buffer_size = 100; + + char* activity_list_buffer = (char*)memops::alloc(activity_list_buffer_size); + memops::zero(activity_list_buffer, activity_list_buffer_size); + u32 activity_list_buffer_cursor = 0; + + snprintf(activity_list_buffer, activity_list_buffer_size, "<ActivityList>"); // @TODO replace with strops function + activity_list_buffer_cursor += strlen(activity_list_buffer); + + for (u32 i = 0; i < activity_count; i++) + { + activity ac; + a_err found = administration::activity_get_by_index(i, &ac); + if (found != A_ERR_SUCCESS) continue; + + int buf_length = 0; + char* file_content = copy_template(file_template::activity_template, &buf_length); + + strops::replace_int32(file_content, buf_length, "{{TIMESTAMP}}", ac.timestamp); + strops::replace(file_content, buf_length, "{{USERNAME}}", ac.user_name); + strops::replace(file_content, buf_length, "{{REF_ID}}", ac.ref_id); + strops::replace(file_content, buf_length, "{{MESSAGE}}", ac.message); + + u32 content_len = (u32)strops::length(file_content); + memops::copy(activity_list_buffer+activity_list_buffer_cursor, file_content, content_len); + + activity_list_buffer_cursor += content_len; + } + + snprintf(activity_list_buffer+activity_list_buffer_cursor, activity_list_buffer_size-activity_list_buffer_cursor, "</ActivityList>"); + + //// Write to Disk. + bool result = 1; + int final_length = (int)strops::length(activity_list_buffer); + if (!xml_string_is_valid((uint8_t*)activity_list_buffer, final_length)) result = 0; + else if (!write_to_zip(ACTIVITY_FILE, activity_list_buffer, final_length)) result = 0; + + memops::unalloc(activity_list_buffer); + + if (result) logger::info("Saved activity list in %.3fms.", STOPWATCH_TIME); + else logger::error("Failed to save activity list."); + + return result; }
\ No newline at end of file diff --git a/src/main_linux.cpp b/src/main_linux.cpp index 867073a..1d05aff 100644 --- a/src/main_linux.cpp +++ b/src/main_linux.cpp @@ -114,18 +114,14 @@ int main(int argc, char** argv) glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); - // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), - // you may need to backup/reset/restore other state, e.g. for current shader using the commented lines below. - //GLint last_program; - //glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - //glUseProgram(0); ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); - //glUseProgram(last_program); glfwMakeContextCurrent(window); glfwSwapBuffers(window); } + administration_writer::save_activities_blocking(); + administration_writer::destroy(); timer_lib_shutdown(); administration::destroy(); diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp index e9af66e..78e5b31 100644 --- a/src/ui/ui_invoices.cpp +++ b/src/ui/ui_invoices.cpp @@ -482,8 +482,8 @@ static void draw_send_options() if (status == E_ERR_SUCCESS) { active_invoice.extras.status = invoice_status::INVOICE_SENT; administration::invoice_update(&active_invoice); - administration::activity_add(ACTIVITY_USER, active_invoice.id, "Invoice status changed", 0); // @locale - administration::activity_add(ACTIVITY_USER, active_invoice.id, "Sent email", 0); // @locale + administration::activity_add(ACTIVITY_USER, active_invoice.id, "Invoice status changed"); // @locale + administration::activity_add(ACTIVITY_USER, active_invoice.id, "Sent email"); // @locale _reload_activities(); } |
