#include #include #include #include #include #include "strops.hpp" #include "administration.hpp" administration g_administration; static s32 administration_create_id() { return g_administration.next_id; } #define ADD_BRACKET(_country, _rate, _description)\ {\ country_tax_bracket* tb = (country_tax_bracket*)malloc(sizeof(country_tax_bracket));\ snprintf(tb->id, sizeof(tb->id), "T/%d", administration_create_id());\ memcpy(tb->country_code, _country, sizeof(tb->country_code));\ tb->rate = _rate;\ memcpy(tb->description, _description, sizeof(tb->description));\ list_append(&g_administration.tax_brackets, tb);\ g_administration.next_id++;\ } static int compare_tax_countries(const void *a, const void *b) { country_tax_bracket *objA = (country_tax_bracket *)a; country_tax_bracket *objB = (country_tax_bracket *)b; return strcmp(objA->country_code, objB->country_code); } static void administration_create_default_tax_brackets() { // General brackets shared between countries. ADD_BRACKET("00", 0.0f, "tax.reverse_charge"); ADD_BRACKET("00", 0.0f, "tax.exempt"); // Austria ADD_BRACKET("AT", 20.0f, "tax.standard"); ADD_BRACKET("AT", 10.0f, "tax.reduced"); ADD_BRACKET("AT", 13.0f, "tax.reduced"); // Belgium ADD_BRACKET("BE", 21.0f, "tax.standard"); ADD_BRACKET("BE", 6.0f, "tax.reduced"); ADD_BRACKET("BE", 12.0f, "tax.reduced"); // Bulgaria ADD_BRACKET("BG", 20.0f, "tax.standard"); ADD_BRACKET("BG", 9.0f, "tax.reduced"); // Cyprus ADD_BRACKET("CY", 19.0f, "tax.standard"); ADD_BRACKET("CY", 5.0f, "tax.reduced"); ADD_BRACKET("CY", 9.0f, "tax.reduced"); // Czechia ADD_BRACKET("CZ", 21.0f, "tax.standard"); ADD_BRACKET("CZ", 12.0f, "tax.reduced"); // Croatia ADD_BRACKET("HR", 25.0f, "tax.standard"); ADD_BRACKET("HR", 5.0f, "tax.reduced"); ADD_BRACKET("HR", 13.0f, "tax.reduced"); // Denmark ADD_BRACKET("DK", 25.0f, "tax.standard"); // Estonia ADD_BRACKET("EE", 22.0f, "tax.standard"); ADD_BRACKET("EE", 9.0f, "tax.reduced"); // Finland ADD_BRACKET("FI", 25.5f, "tax.standard"); ADD_BRACKET("FI", 10.0f, "tax.reduced"); ADD_BRACKET("FI", 14.0f, "tax.reduced"); // France ADD_BRACKET("FR", 20.0f, "tax.standard"); ADD_BRACKET("FR", 5.5f, "tax.reduced"); ADD_BRACKET("FR", 10.0f, "tax.reduced"); ADD_BRACKET("FR", 2.1f, "tax.superReduced"); // Germany ADD_BRACKET("DE", 19.0f, "tax.standard"); ADD_BRACKET("DE", 7.0f, "tax.reduced"); // Greece ADD_BRACKET("GR", 24.0f, "tax.standard"); ADD_BRACKET("GR", 6.0f, "tax.reduced"); ADD_BRACKET("GR", 13.0f, "tax.reduced"); // Hungary ADD_BRACKET("HU", 27.0f, "tax.standard"); ADD_BRACKET("HU", 5.0f, "tax.reduced"); ADD_BRACKET("HU", 18.0f, "tax.reduced"); // Ireland ADD_BRACKET("IE", 23.0f, "tax.standard"); ADD_BRACKET("IE", 9.0f, "tax.reduced"); ADD_BRACKET("IE", 13.5f, "tax.reduced"); ADD_BRACKET("IE", 4.8f, "tax.superReduced"); // Italy ADD_BRACKET("IT", 22.0f, "tax.standard"); ADD_BRACKET("IT", 5.0f, "tax.reduced"); ADD_BRACKET("IT", 10.0f, "tax.reduced"); ADD_BRACKET("IT", 4.0f, "tax.superReduced"); // Latvia ADD_BRACKET("LV", 21.0f, "tax.standard"); ADD_BRACKET("LV", 5.0f, "tax.reduced"); ADD_BRACKET("LV", 12.0f, "tax.reduced"); // Lithuania ADD_BRACKET("LT", 21.0f, "tax.standard"); ADD_BRACKET("LT", 5.0f, "tax.reduced"); ADD_BRACKET("LT", 9.0f, "tax.reduced"); // Luxembourg ADD_BRACKET("LU", 17.0f, "tax.standard"); ADD_BRACKET("LU", 8.0f, "tax.reduced"); ADD_BRACKET("LU", 14.0f, "tax.reduced"); ADD_BRACKET("LU", 3.0f, "tax.superReduced"); // Malta ADD_BRACKET("MT", 18.0f, "tax.standard"); ADD_BRACKET("MT", 5.0f, "tax.reduced"); ADD_BRACKET("MT", 7.0f, "tax.reduced"); // Netherlands ADD_BRACKET("NL", 21.0f, "tax.standard"); ADD_BRACKET("NL", 9.0f, "tax.reduced"); // Poland ADD_BRACKET("PL", 23.0f, "tax.standard"); ADD_BRACKET("PL", 5.0f, "tax.reduced"); ADD_BRACKET("PL", 8.0f, "tax.reduced"); // Portugal ADD_BRACKET("PT", 23.0f, "tax.standard"); ADD_BRACKET("PT", 6.0f, "tax.reduced"); ADD_BRACKET("PT", 13.0f, "tax.reduced"); // Romania ADD_BRACKET("RO", 19.0f, "tax.standard"); ADD_BRACKET("RO", 5.0f, "tax.reduced"); ADD_BRACKET("RO", 9.0f, "tax.reduced"); // Slovakia ADD_BRACKET("SK", 23.0f, "tax.standard"); ADD_BRACKET("SK", 5.0f, "tax.reduced"); ADD_BRACKET("SK", 19.0f, "tax.reduced"); // Slovenia ADD_BRACKET("SI", 22.0f, "tax.standard"); ADD_BRACKET("SI", 5.0f, "tax.reduced"); ADD_BRACKET("SI", 9.5f, "tax.reduced"); // Spain ADD_BRACKET("ES", 21.0f, "tax.standard"); ADD_BRACKET("ES", 10.0f, "tax.reduced"); ADD_BRACKET("ES", 4.0f, "tax.superReduced"); // Sweden ADD_BRACKET("SE", 25.0f, "tax.standard"); ADD_BRACKET("SE", 6.0f, "tax.reduced"); ADD_BRACKET("SE", 12.0f, "tax.reduced"); list_attributes_comparator(&g_administration.tax_brackets, compare_tax_countries); list_sort(&g_administration.tax_brackets, -1); } static void administration_create_default_cost_centers() { #define ADD_COSTCENTER(_description, _code)\ {\ cost_center* tb = (cost_center*)malloc(sizeof(cost_center));\ snprintf(tb->id, sizeof(tb->id), "E/%d", administration_create_id());\ memcpy(tb->description, _description, sizeof(tb->description));\ memcpy(tb->code, _code, sizeof(tb->code));\ list_append(&g_administration.cost_centers, tb);\ g_administration.next_id++;\ } ADD_COSTCENTER("costcenter.general_expenses", "GENE"); ADD_COSTCENTER("costcenter.administration_general_management", "ADMN"); ADD_COSTCENTER("costcenter.finance_accounting", "FINC"); ADD_COSTCENTER("costcenter.information_technology", "INFO"); ADD_COSTCENTER("costcenter.sales_marketing", "SALE"); ADD_COSTCENTER("costcenter.operations_production", "OPER"); ADD_COSTCENTER("costcenter.supply_chain_logistics", "SUPP"); ADD_COSTCENTER("costcenter.research_development", "RDEV"); ADD_COSTCENTER("costcenter.facilities_maintenance", "FACL"); ADD_COSTCENTER("costcenter.customer_service_support", "CUST"); ADD_COSTCENTER("costcenter.other_specialized", "OTHR"); } void administration_create() { g_administration.next_id = 1; g_administration.next_sequence_number = 1; strops_copy(g_administration.company_info.name, "Aldrik Ramaekers", sizeof(g_administration.company_info.name)); strops_copy(g_administration.company_info.address.address1, "Keerderstraat 81", sizeof(g_administration.company_info.address.address1)); strops_copy(g_administration.company_info.address.address2, "6226XW Maastricht", sizeof(g_administration.company_info.address.address2)); strops_copy(g_administration.company_info.address.country_code, "NL", sizeof(g_administration.company_info.address.country_code)); list_init(&g_administration.contacts); list_init(&g_administration.projects); list_init(&g_administration.tax_brackets); list_init(&g_administration.cost_centers); strops_copy(g_administration.path, "", sizeof(g_administration.path)); snprintf(g_administration.company_info.id, sizeof(g_administration.company_info.id), "C/%d", administration_create_id()); g_administration.next_id++; administration_create_default_tax_brackets(); administration_create_default_cost_centers(); } void administration_destroy() { list_destroy(&g_administration.contacts); list_destroy(&g_administration.projects); list_destroy(&g_administration.tax_brackets); list_destroy(&g_administration.cost_centers); } bool administration_create_contact(contact data) { contact* new_contact = (contact*)malloc(sizeof(contact)); memcpy((void*)new_contact, (void*)&data, sizeof(contact)); list_append(&g_administration.contacts, new_contact); g_administration.next_id++; return true; } bool administration_can_contact_be_deleted(contact data) { (void)data; // TODO return true; } bool administration_update_contact(contact data) { list_iterator_start(&g_administration.contacts); while (list_iterator_hasnext(&g_administration.contacts)) { contact* c = (contact *)list_iterator_next(&g_administration.contacts); if (strcmp(c->id, data.id) == 0) { memcpy(c, &data, sizeof(data)); list_iterator_stop(&g_administration.contacts); return true; } } list_iterator_stop(&g_administration.contacts); return false; } bool administration_remove_contact(contact data) { list_iterator_start(&g_administration.contacts); while (list_iterator_hasnext(&g_administration.contacts)) { contact* c = (contact *)list_iterator_next(&g_administration.contacts); if (strcmp(c->id, data.id) == 0) { list_iterator_stop(&g_administration.contacts); list_delete(&g_administration.contacts, c); return true; } } list_iterator_stop(&g_administration.contacts); return false; } u32 administration_get_contact_count() { return list_size(&g_administration.contacts); } u32 administration_get_contacts(u32 page_index, u32 page_size, contact* buffer) { assert(buffer); u32 write_cursor = 0; u32 read_start = page_index * page_size; list_iterator_start(&g_administration.contacts); while (list_iterator_hasnext(&g_administration.contacts)) { contact c = *(contact *)list_iterator_next(&g_administration.contacts); if (g_administration.contacts.iter_pos <= read_start) continue; buffer[write_cursor++] = c; if (write_cursor >= page_size) break; } list_iterator_stop(&g_administration.contacts); return write_cursor; } char* administration_get_file_path() { return g_administration.path; } u32 administration_get_project_count() { return list_size(&g_administration.projects); } u32 administration_get_projects(u32 page_index, u32 page_size, project* buffer) { assert(buffer); u32 write_cursor = 0; u32 read_start = page_index * page_size; list_iterator_start(&g_administration.projects); while (list_iterator_hasnext(&g_administration.projects)) { project c = *(project *)list_iterator_next(&g_administration.projects); if (g_administration.projects.iter_pos <= read_start) continue; buffer[write_cursor++] = c; if (write_cursor >= page_size) break; } list_iterator_stop(&g_administration.projects); return write_cursor; } void administration_cancel_project(project data) { data.end_date = time(NULL); data.state = project_state::PROJECT_CANCELLED; administration_update_project(data); } char* administration_project_get_status_string(project data) { switch(data.state) { case project_state::PROJECT_RUNNING: return "project.state.running"; case project_state::PROJECT_PAUSED: return "project.state.paused"; case project_state::PROJECT_CANCELLED: return "project.state.cancelled"; default: assert(0); break; } return ""; } bool administration_create_project(project data) { data.state = project_state::PROJECT_RUNNING; data.start_date = time(NULL); data.end_date = 0; project* new_project = (project*)malloc(sizeof(project)); memcpy((void*)new_project, (void*)&data, sizeof(project)); list_append(&g_administration.projects, new_project); g_administration.next_id++; return true; } bool administration_update_project(project data) { list_iterator_start(&g_administration.projects); while (list_iterator_hasnext(&g_administration.projects)) { project* c = (project *)list_iterator_next(&g_administration.projects); if (strcmp(c->id, data.id) == 0) { memcpy(c, &data, sizeof(data)); list_iterator_stop(&g_administration.projects); return true; } } list_iterator_stop(&g_administration.projects); return false; } bool administration_remove_project(project data) { list_iterator_start(&g_administration.projects); while (list_iterator_hasnext(&g_administration.projects)) { project* c = (project *)list_iterator_next(&g_administration.projects); if (strcmp(c->id, data.id) == 0) { list_iterator_stop(&g_administration.projects); list_delete(&g_administration.projects, c); return true; } } list_iterator_stop(&g_administration.projects); return false; } contact administration_get_company_info() { return g_administration.company_info; } void administration_set_company_info(contact data) { g_administration.company_info = data; } u32 administration_get_tax_bracket_count() { return list_size(&g_administration.tax_brackets); } bool administration_add_tax_bracket(country_tax_bracket data) { ADD_BRACKET(data.country_code, data.rate, ""); list_attributes_comparator(&g_administration.tax_brackets, compare_tax_countries); list_sort(&g_administration.tax_brackets, -1); return true; } u32 administration_get_tax_brackets(country_tax_bracket* buffer) { assert(buffer); u32 write_cursor = 0; list_iterator_start(&g_administration.tax_brackets); while (list_iterator_hasnext(&g_administration.tax_brackets)) { country_tax_bracket c = *(country_tax_bracket *)list_iterator_next(&g_administration.tax_brackets); buffer[write_cursor++] = c; } list_iterator_stop(&g_administration.tax_brackets); return write_cursor; } bool administration_update_tax_bracket(country_tax_bracket data) { list_iterator_start(&g_administration.tax_brackets); while (list_iterator_hasnext(&g_administration.tax_brackets)) { country_tax_bracket* c = (country_tax_bracket *)list_iterator_next(&g_administration.tax_brackets); if (strcmp(c->id, data.id) == 0) { memcpy(c, &data, sizeof(data)); list_iterator_stop(&g_administration.tax_brackets); return true; } } list_iterator_stop(&g_administration.tax_brackets); return false; } u32 administration_get_cost_center_count() { return list_size(&g_administration.cost_centers); } u32 administration_get_cost_centers(cost_center* buffer) { assert(buffer); u32 write_cursor = 0; list_iterator_start(&g_administration.cost_centers); while (list_iterator_hasnext(&g_administration.cost_centers)) { cost_center c = *(cost_center *)list_iterator_next(&g_administration.cost_centers); buffer[write_cursor++] = c; } list_iterator_stop(&g_administration.cost_centers); return write_cursor; } static bool administration_get_cost_center_by_code(char* code, cost_center* buffer) { bool result = false; list_iterator_start(&g_administration.cost_centers); while (list_iterator_hasnext(&g_administration.cost_centers)) { cost_center c = *(cost_center *)list_iterator_next(&g_administration.cost_centers); *buffer = c; if (strcmp(code, c.code) == 0) { result = true; break; } } list_iterator_stop(&g_administration.cost_centers); return result; } bool administration_verify_cost_center_description(char* text) { return strlen(text) != 0; } bool administration_verify_cost_center_code(char* code) { if (strlen(code) == 0) return false; cost_center cost_center; bool found = administration_get_cost_center_by_code(code, &cost_center); return !found; } bool administration_add_cost_center(cost_center data) { cost_center cs; bool found = administration_get_cost_center_by_code(data.code, &cs); if (found) return false; cost_center* tb = (cost_center*)malloc(sizeof(cost_center)); snprintf(tb->id, sizeof(tb->id), "E/%d", administration_create_id()); memcpy(tb->description, data.description, sizeof(tb->description)); memcpy(tb->code, data.code, sizeof(tb->code)); list_append(&g_administration.cost_centers, tb); g_administration.next_id++; return true; } bool administration_update_cost_center(cost_center data) { list_iterator_start(&g_administration.cost_centers); while (list_iterator_hasnext(&g_administration.cost_centers)) { cost_center* c = (cost_center *)list_iterator_next(&g_administration.cost_centers); if (strcmp(c->id, data.id) == 0) { memcpy(c, &data, sizeof(data)); list_iterator_stop(&g_administration.cost_centers); return true; } } list_iterator_stop(&g_administration.cost_centers); return false; } bool administration_is_contact_valid(contact data) { if (data.type == contact_type::CONTACT_CONSUMER) { return strlen(data.name) > 0 && strlen(data.address.address1) > 0 && strlen(data.address.address2) > 0 && strlen(data.address.country_code) > 0; } else if (data.type == contact_type::CONTACT_BUSINESS) { return strlen(data.name) > 0 && strlen(data.address.address1) > 0 && strlen(data.address.address2) > 0 && strlen(data.address.country_code) > 0 && strlen(data.taxid) > 0 && strlen(data.businessid); } return false; } static s32 administration_create_sequence_number() { return g_administration.next_sequence_number; } invoice administration_create_empty_invoice() { invoice result; memset(&result, 0, sizeof(invoice)); snprintf(result.id, sizeof(result.id), "I/%d", administration_create_id()); snprintf(result.sequential_number, sizeof(result.id), "INV%010d", administration_create_sequence_number()); return result; } contact administration_create_empty_contact() { contact result; memset(&result, 0, sizeof(contact)); snprintf(result.id, sizeof(result.id), "C/%d", administration_create_id()); return result; } project administration_create_empty_project() { project result; memset(&result, 0, sizeof(project)); snprintf(result.id, sizeof(result.id), "P/%d", administration_create_id()); return result; }