summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/CHANGES.rst3
-rw-r--r--include/administration.hpp3
-rw-r--r--include/ui.hpp3
-rw-r--r--src/administration.cpp4
-rw-r--r--src/countries/nl.cpp50
-rw-r--r--src/locales/en.cpp26
-rw-r--r--src/ui/ui_main.cpp6
-rw-r--r--src/ui/ui_tax.cpp111
-rw-r--r--tests/nl_tax_tests.cpp8
9 files changed, 181 insertions, 33 deletions
diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst
index c7a020b..a31ff8a 100644
--- a/docs/CHANGES.rst
+++ b/docs/CHANGES.rst
@@ -1,5 +1,8 @@
.. _changes:
+INCORRECT DATA:
+- invoices should only accept the default currency. if importing an invoice with another currency, it should be converted manually. (due to tax reporting mixing currencies atm.)
+
TODO:
for invoice importing using AI:
1. all address data should be editable because import is not perfect
diff --git a/include/administration.hpp b/include/administration.hpp
index 69c0c0a..f2ca9e4 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -330,6 +330,7 @@ typedef struct
typedef struct
{
+ char tax_description[MAX_LEN_LONG_DESC];
char tax_category[MAX_LEN_SHORT_DESC];
float total_net;
float total_tax;
@@ -347,7 +348,7 @@ typedef struct
typedef struct
{
- u32 quarter_count;
+ u32 report_count;
tax_report reports[MAX_LEN_TAX_REPORT_QUARTERS];
} tax_statement;
diff --git a/include/ui.hpp b/include/ui.hpp
index 9304f78..0f71c1e 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -77,6 +77,7 @@ namespace ui {
void draw_expenses();
void draw_earnings();
void draw_log();
+ void draw_tax_report();
// Setup calls.
void setup_invoices();
@@ -85,12 +86,14 @@ namespace ui {
void setup_settings();
void setup_expenses();
void setup_earnings();
+ void setup_tax_report();
// Destroy calls.
void destroy_invoices();
void destroy_settings();
void destroy_expenses();
void destroy_earnings();
+ void destroy_tax_report();
}
diff --git a/src/administration.cpp b/src/administration.cpp
index 0ad9684..5675cb4 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -484,7 +484,7 @@ void administration::create_tax_statement(tax_statement* statement)
char* country_code = company_info_get().address.country_code;
assert(statement);
- statement->quarter_count = 0;
+ statement->report_count = 0;
u32 invoice_count = administration::invoice_count();
if (invoice_count == 0) return;
@@ -526,7 +526,7 @@ void administration::create_tax_statement(tax_statement* statement)
country::fill_tax_report_with_categories(country_code, &quarter);
- statement->reports[statement->quarter_count++] = quarter;
+ statement->reports[statement->report_count++] = quarter;
}
// Fill quarters.
diff --git a/src/countries/nl.cpp b/src/countries/nl.cpp
index 9c8a20b..bc45b59 100644
--- a/src/countries/nl.cpp
+++ b/src/countries/nl.cpp
@@ -26,25 +26,30 @@ time_t _nl_get_default_invoice_expire_duration()
void _nl_fill_tax_report_with_categories(tax_report* report)
{
- report->lines[report->line_count++] = tax_line {"1a", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"1b", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"1c", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"1d", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"1e", 0.0f, 0.0f};
-
- report->lines[report->line_count++] = tax_line {"2a", 0.0f, 0.0f};
-
- report->lines[report->line_count++] = tax_line {"3a", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"3b", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"3c", 0.0f, 0.0f};
-
- report->lines[report->line_count++] = tax_line {"4a", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"4b", 0.0f, 0.0f};
-
- report->lines[report->line_count++] = tax_line {"5a", 0.0f, 0.0f};
- report->lines[report->line_count++] = tax_line {"5b", 0.0f, 0.0f};
-
- report->lines[report->line_count++] = tax_line {"Total", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1", "", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1a", "1a", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1b", "1b", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1c", "1c", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1d", "1d", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.1e", "1e", 0.0f, 0.0f};
+
+ report->lines[report->line_count++] = tax_line {"taxes.nl.2", "", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.2a", "2a", 0.0f, 0.0f};
+
+ report->lines[report->line_count++] = tax_line {"taxes.nl.3", "", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.3a", "3a", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.3b", "3b", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.3c", "3c", 0.0f, 0.0f};
+
+ report->lines[report->line_count++] = tax_line {"taxes.nl.4", "", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.4a", "4a", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.4b", "4b", 0.0f, 0.0f};
+
+ report->lines[report->line_count++] = tax_line {"taxes.nl.5", "", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.5a", "5a", 0.0f, 0.0f};
+ report->lines[report->line_count++] = tax_line {"taxes.nl.5b", "5b", 0.0f, 0.0f};
+
+ report->lines[report->line_count++] = tax_line {"taxes.total", "5c", 0.0f, 0.0f};
}
bool _nl_add_billing_item_to_tax_report(tax_report* report, invoice* inv, billing_item* item)
@@ -108,7 +113,7 @@ bool _nl_add_billing_item_to_tax_report(tax_report* report, invoice* inv, billin
tax_line* a5 = administration::get_tax_line_from_report(report, "5a"); // Total owed.
tax_line* b5 = administration::get_tax_line_from_report(report, "5b"); // Input tax.
- if (strops::equals(inv->customer.address.country_code, "NL")) {
+ if (strops::equals(inv->supplier.address.country_code, "NL")) {
if (strops::equals(rate.category_code, "AE")) { // NL reverse charge.
tax_line* tl = administration::get_tax_line_from_report(report, "2a");
@@ -123,8 +128,7 @@ bool _nl_add_billing_item_to_tax_report(tax_report* report, invoice* inv, billin
b5->total_tax += item->tax;
}
}
-
- if (!country::is_EU(inv->customer.address.country_code)) {
+ else if (!country::is_EU(inv->supplier.address.country_code)) {
tax_line* tl = administration::get_tax_line_from_report(report, "4a");
tl->total_net += item->net;
@@ -151,7 +155,7 @@ float _nl_calculate_tax_report_final(tax_report* report)
{
tax_line* a5 = administration::get_tax_line_from_report(report, "5a"); // Total owed.
tax_line* b5 = administration::get_tax_line_from_report(report, "5b"); // Input tax.
- tax_line* total = administration::get_tax_line_from_report(report, "Total");
+ tax_line* total = administration::get_tax_line_from_report(report, "5c");
total->total_tax = a5->total_tax - b5->total_tax;
return (float)ceil(total->total_tax);
diff --git a/src/locales/en.cpp b/src/locales/en.cpp
index e7ad41b..e68e930 100644
--- a/src/locales/en.cpp
+++ b/src/locales/en.cpp
@@ -208,6 +208,32 @@ locale_entry en_locales[] = {
{"import.error.upload","Failure: Upload failed"},
{"import.error.query","Failure: Querying service failed"},
{"import.error.import","Failure: Failed to import result from service"},
+
+ // Tax statement strings.
+ {"taxes.total", "Total"},
+
+ {"taxes.nl.1", "Domestic supplies and services"},
+ {"taxes.nl.1a", "Supplies/services taxed at the high rate"},
+ {"taxes.nl.1b", "Supplies/services taxed at the reduced rate"},
+ {"taxes.nl.1c", "Supplies/services taxed at other rates (excluding 0%)"},
+ {"taxes.nl.1d", "Private use"},
+ {"taxes.nl.1e", "Supplies/services taxed at 0% or not taxed with you"},
+
+ {"taxes.nl.2", "Reverse-charge arrangements within the Netherlands"},
+ {"taxes.nl.2a", "Supplies/services for which VAT has been reverse-charged to you"},
+
+ {"taxes.nl.3", "Supplies and services to or in other countries"},
+ {"taxes.nl.3a", "Supplies to countries outside the EU (exports)"},
+ {"taxes.nl.3b", "Supplies to or services in EU countries"},
+ {"taxes.nl.3c", "Intra-EU distance sales and installation supplies"},
+
+ {"taxes.nl.4", "Services supplied to you from abroad"},
+ {"taxes.nl.4a", "Supplies/services received from countries outside the EU (imports)"},
+ {"taxes.nl.4b", "Supplies/services received from countries within the EU (intra-EU acquisitions)"},
+
+ {"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)"},
};
int en_locale_count = sizeof(en_locales) / sizeof(en_locales[0]); \ No newline at end of file
diff --git a/src/ui/ui_main.cpp b/src/ui/ui_main.cpp
index 9e6aeb8..cf30840 100644
--- a/src/ui/ui_main.cpp
+++ b/src/ui/ui_main.cpp
@@ -28,7 +28,7 @@ void (*drawcalls[ui::main_state::UI_END])(void) = {
ui::draw_expenses,
ui::draw_contacts,
ui::draw_earnings,
- 0,
+ ui::draw_tax_report,
ui::draw_projects,
ui::draw_settings,
ui::draw_log,
@@ -39,7 +39,7 @@ void (*setupcalls[ui::main_state::UI_END])(void) = {
ui::setup_expenses,
ui::setup_contacts,
ui::setup_earnings,
- 0,
+ ui::setup_tax_report,
ui::setup_projects,
ui::setup_settings,
0,
@@ -50,7 +50,7 @@ void (*destroycalls[ui::main_state::UI_END])(void) = {
ui::destroy_expenses,
0,
ui::destroy_earnings,
- 0,
+ ui::destroy_tax_report,
0,
ui::destroy_settings,
0,
diff --git a/src/ui/ui_tax.cpp b/src/ui/ui_tax.cpp
new file mode 100644
index 0000000..37eeaee
--- /dev/null
+++ b/src/ui/ui_tax.cpp
@@ -0,0 +1,111 @@
+/*
+* Copyright (c) 2025 Aldrik Ramaekers <aldrik.ramaekers@gmail.com>
+*
+* Permission to use, copy, modify, and/or distribute this software for any
+* purpose with or without fee is hereby granted, provided that the above
+* copyright notice and this permission notice appear in all copies.
+*
+* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+
+#include "ui.hpp"
+#include "imgui.h"
+#include "memops.hpp"
+#include "strops.hpp"
+#include "locales.hpp"
+#include "administration.hpp"
+
+tax_statement* statement = 0;
+
+void ui::setup_tax_report()
+{
+ statement = (tax_statement*)memops::alloc(sizeof(tax_statement));
+ administration::create_tax_statement(statement);
+}
+
+void ui::destroy_tax_report()
+{
+ memops::unalloc(statement);
+}
+
+void ui::draw_tax_report()
+{
+ static s32 current_page = 0;
+ s32 max_page = statement->report_count;
+
+ if (current_page >= max_page-1) current_page = max_page-1;
+ if (current_page < 0) current_page = 0;
+
+ bool enable_prev = current_page > 0;
+ if (!enable_prev) ImGui::BeginDisabled();
+ if (ImGui::Button(locale::get("ui.prev")) && current_page > 0) current_page--;
+ if (!enable_prev) ImGui::EndDisabled();
+
+ ImGui::SameLine();
+ ImGui::Text("(%d/%d)", current_page+1, max_page);
+
+ ImGui::SameLine();
+ bool enable_next = current_page < max_page-1;
+ if (!enable_next) ImGui::BeginDisabled();
+ if (ImGui::Button(locale::get("ui.next")) && current_page < max_page-1) current_page++;
+ if (!enable_next) ImGui::EndDisabled();
+
+ ImGui::Spacing();
+
+ char* currency_symbol = administration::get_currency_symbol_for_currency(administration::get_default_currency());
+ tax_report report = statement->reports[current_page];
+
+ if (ImGui::BeginTable("QuarterlyTaxTable", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Hideable))
+ {
+ ImGui::PushFont(fontBold);
+ ImGui::TableSetupColumn("##desc", ImGuiTableColumnFlags_WidthStretch);
+ ImGui::TableSetupColumn("##names", ImGuiTableColumnFlags_WidthFixed, 150);
+ ImGui::TableSetupColumn(report.quarter_str, ImGuiTableColumnFlags_WidthFixed, 150);
+ ImGui::TableSetupColumn("##tax", ImGuiTableColumnFlags_WidthFixed, 150);
+
+ ImGui::TableHeadersRow();
+ ImGui::PopFont();
+
+ for (u32 x = 0; x < report.line_count; x++)
+ {
+ tax_line line = report.lines[x];
+ bool is_last = x == (report.line_count-1);
+ bool bold = is_last || strops::equals(line.tax_category, "");
+
+ if (bold) ImGui::PushFont(fontBold);
+
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0); ImGui::Text("%s", locale::get(line.tax_description));
+ ImGui::TableSetColumnIndex(1); ImGui::Text("%s", locale::get(line.tax_category));
+
+ if (!strops::equals(line.tax_category, "")) {
+ ImGui::TableSetColumnIndex(2); ImGui::Text("%.2f %s", line.total_net, currency_symbol);
+
+
+ ImGui::TableSetColumnIndex(3);
+ if (!is_last) ImGui::Text("%.2f %s", line.total_tax, currency_symbol);
+ else {
+ if (line.total_tax < 0.0f) {
+ ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(235, 64, 52, 255));
+ ImGui::Text("(%.0f %s)", line.total_tax, currency_symbol);
+ ImGui::PopStyleColor();
+ }
+ else {
+ ImGui::Text("%.0f %s", line.total_tax, currency_symbol);
+ }
+ }
+ }
+
+ if (bold) ImGui::PopFont();
+ }
+
+ ImGui::EndTable();
+ }
+} \ No newline at end of file
diff --git a/tests/nl_tax_tests.cpp b/tests/nl_tax_tests.cpp
index e6fda94..bd7b51d 100644
--- a/tests/nl_tax_tests.cpp
+++ b/tests/nl_tax_tests.cpp
@@ -13,7 +13,7 @@ TEST _nl_tax_1a(void)
tax_statement statement;
administration::create_tax_statement(&statement);
- ASSERT_EQ(statement.quarter_count, 1);
+ ASSERT_EQ(statement.report_count, 1);
tax_line* tl = administration::get_tax_line_from_report(&statement.reports[0], "1a");
ASSERT_EQ(tl->total_net, 50.0f);
@@ -38,7 +38,7 @@ TEST _nl_tax_1b(void)
tax_statement statement;
administration::create_tax_statement(&statement);
- ASSERT_EQ(statement.quarter_count, 1);
+ ASSERT_EQ(statement.report_count, 1);
tax_line* tl = administration::get_tax_line_from_report(&statement.reports[0], "1b");
ASSERT_EQ(tl->total_net, 25.0f);
@@ -69,7 +69,7 @@ TEST _nl_tax_1d(void)
tax_statement statement;
administration::create_tax_statement(&statement);
- ASSERT_EQ(statement.quarter_count, 1);
+ ASSERT_EQ(statement.report_count, 1);
tax_line* tl = administration::get_tax_line_from_report(&statement.reports[0], "1d");
ASSERT_EQ(tl->total_net, 45.0f);
@@ -94,7 +94,7 @@ TEST _nl_tax_1e(void)
tax_statement statement;
administration::create_tax_statement(&statement);
- ASSERT_EQ(statement.quarter_count, 1);
+ ASSERT_EQ(statement.report_count, 1);
tax_line* tl = administration::get_tax_line_from_report(&statement.reports[0], "1e");
ASSERT_EQ(tl->total_net, 80.0f);