Add superuser invoice distribution module:

- Integrate departmental distribution view with monthly breakdowns, filters, and metrics.
- Add support for comparisons, source blends, and allocation trends.
- Update i18n translations for English, Swedish, German, and Norwegian.
- Extend router with distribution-specific routes.
- Add unit tests for route mapping, tabs, and view integrations.
- Update e2e tests for API routes.
This commit is contained in:
Jeppe Bundgaard
2026-03-12 17:34:26 +01:00
parent 203123e73b
commit db2040570e
9 changed files with 953 additions and 33 deletions
+146
View File
@@ -3920,5 +3920,151 @@
"retry_now": "Prøver at genoprette forbindelsen...",
"retry": "Prøv igen",
"footer": "Hvis problemet fortsætter, kontakt venligst support."
},
"superuser_invoice_distribution": {
"tab_label": "Distribution",
"overview": {
"title": "Departmental Distribution",
"subtitle": "Monthly overview of shared income allocation by source.",
"loaded_months": "{loaded} months loaded"
},
"monthly_page": {
"title": "Distribution for {month}",
"subtitle": "Review departmental allocations, customer split, and invoice comparisons.",
"toolbar_month": "Month selector",
"toolbar_month_picker": "Select month"
},
"actions": {
"reload": "Reload",
"open_month": "Open month",
"try_again": "Try again",
"reset_filters": "Reset filters",
"previous_month": "Go to previous month",
"next_month": "Go to next month",
"close": "Close"
},
"filters": {
"search": "Search month",
"search_placeholder": "Find month...",
"year": "Year",
"all_years": "All years",
"search_department": "Search department",
"search_department_placeholder": "Department name...",
"search_customer": "Search customer",
"search_customer_placeholder": "Customer name or number...",
"source": "Source",
"all_sources": "All sources",
"department": "Department",
"all_departments": "All departments"
},
"metrics": {
"total": "Total",
"booked": "Booked invoices",
"distribution": "Departmental distribution",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Vehicle subscriptions",
"other": "Other booked income",
"months": "Months in view"
},
"tabs": {
"overview": "Overview",
"departments": "Departments",
"customers": "Customers",
"compare": "Compare"
},
"table": {
"month": "Month",
"actions": "Actions",
"metric": "Metric",
"current": "Current",
"compared": "Compared month",
"delta": "Delta",
"department": "Department",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Subscriptions",
"total": "Total",
"share": "Share",
"estimated_booked_allocation": "Estimated booked allocation",
"customer": "Customer",
"source": "Source",
"amount": "Amount",
"department_allocations": "Department allocations",
"invoice_id": "Invoice ID",
"internal_total": "Local total",
"economic_total": "E-conomic total",
"warnings": "Warnings",
"status": "Status",
"booked": "Booked",
"distribution": "Distribution",
"source_mix": "Source mix"
},
"tooltips": {
"summary": "Each month includes booked totals plus fixed-pricing and subscription distribution totals.",
"fixed_pricing": "Amount assigned from fixed-price agreements for the selected month.",
"subscriptions": "Amount assigned from vehicle subscriptions for the selected month.",
"estimated_booked": "Estimated booked share based on each department's proportion of the shared distribution.",
"compare_mode": "Invoice total compares aggregate totals. Line-by-line provides per-invoice line summary from available compare data."
},
"errors": {
"overview_load_failed": "Could not load monthly distribution overview",
"month_load_failed": "Could not load selected month",
"compare_failed": "Could not complete comparison"
},
"empty": {
"no_months": "No months to display",
"no_departments": "No departments for this month",
"no_customers": "No customers match the current filters",
"no_compare_results": "No comparison results yet",
"no_comparison_months": "No month available",
"no_month_comparison": "Select a comparison month to view deltas."
},
"loading": {
"loading_months": "Loading months ({loaded}/{total})"
},
"compare": {
"compare_month": "Compare against month",
"month_to_month": "Month-over-month comparison",
"mode": "Comparison mode",
"run": "Compare all local invoices with E-conomic",
"line_mode_disclaimer": "Line-by-line mode uses available compare metadata and warning lines from the API response.",
"total_checked": "Invoices checked",
"mismatches": "Mismatches",
"warnings": "Warnings",
"total_difference": "Total difference",
"modes": {
"invoice_total": "Invoice total only",
"line_by_line": "Line-by-line summary"
},
"status": {
"ok": "Match",
"mismatch": "Mismatch"
},
"run_short": "Run compare",
"progress": "Processing {processed} / {total}",
"sorted_hint": "Rows are sorted by mismatches and warning severity first.",
"warning_details": "Warnings for invoice #{invoiceId}"
},
"sections": {
"trend": "Monthly trend",
"month_table": "Month totals",
"source_blend": "Source blend",
"top_months": "Top months by total"
},
"departments": {
"allocation_hint": "Booked allocation is estimated from each department's distribution share.",
"rows": "departments"
},
"aria": {
"trend_chart": "Distribution trend chart by month",
"month_table_caption": "Monthly distribution totals with source mix and quick-open actions",
"sort_month": "Sort table by month",
"sort_total": "Sort table by total amount",
"source_mix_row": "Source composition for {month}",
"open_month": "Open distribution details for {month}",
"month_comparison_caption": "Month-over-month comparison table for key metrics",
"department_table_caption": "Department-level allocation split for the selected month",
"customer_table_caption": "Customer-level allocation rows for the selected month",
"compare_table_caption": "Invoice comparison results sorted by mismatches and warnings"
}
}
}
+146
View File
@@ -3919,5 +3919,151 @@
"disable_error_title": "Deaktivierung fehlgeschlagen",
"enter_code": "Geben Sie Ihren Bestätigungscode ein",
"back_to_login": "Zurück zur Anmeldung"
},
"superuser_invoice_distribution": {
"tab_label": "Distribution",
"overview": {
"title": "Departmental Distribution",
"subtitle": "Monthly overview of shared income allocation by source.",
"loaded_months": "{loaded} months loaded"
},
"monthly_page": {
"title": "Distribution for {month}",
"subtitle": "Review departmental allocations, customer split, and invoice comparisons.",
"toolbar_month": "Month selector",
"toolbar_month_picker": "Select month"
},
"actions": {
"reload": "Reload",
"open_month": "Open month",
"try_again": "Try again",
"reset_filters": "Reset filters",
"previous_month": "Go to previous month",
"next_month": "Go to next month",
"close": "Close"
},
"filters": {
"search": "Search month",
"search_placeholder": "Find month...",
"year": "Year",
"all_years": "All years",
"search_department": "Search department",
"search_department_placeholder": "Department name...",
"search_customer": "Search customer",
"search_customer_placeholder": "Customer name or number...",
"source": "Source",
"all_sources": "All sources",
"department": "Department",
"all_departments": "All departments"
},
"metrics": {
"total": "Total",
"booked": "Booked invoices",
"distribution": "Departmental distribution",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Vehicle subscriptions",
"other": "Other booked income",
"months": "Months in view"
},
"tabs": {
"overview": "Overview",
"departments": "Departments",
"customers": "Customers",
"compare": "Compare"
},
"table": {
"month": "Month",
"actions": "Actions",
"metric": "Metric",
"current": "Current",
"compared": "Compared month",
"delta": "Delta",
"department": "Department",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Subscriptions",
"total": "Total",
"share": "Share",
"estimated_booked_allocation": "Estimated booked allocation",
"customer": "Customer",
"source": "Source",
"amount": "Amount",
"department_allocations": "Department allocations",
"invoice_id": "Invoice ID",
"internal_total": "Local total",
"economic_total": "E-conomic total",
"warnings": "Warnings",
"status": "Status",
"booked": "Booked",
"distribution": "Distribution",
"source_mix": "Source mix"
},
"tooltips": {
"summary": "Each month includes booked totals plus fixed-pricing and subscription distribution totals.",
"fixed_pricing": "Amount assigned from fixed-price agreements for the selected month.",
"subscriptions": "Amount assigned from vehicle subscriptions for the selected month.",
"estimated_booked": "Estimated booked share based on each department's proportion of the shared distribution.",
"compare_mode": "Invoice total compares aggregate totals. Line-by-line provides per-invoice line summary from available compare data."
},
"errors": {
"overview_load_failed": "Could not load monthly distribution overview",
"month_load_failed": "Could not load selected month",
"compare_failed": "Could not complete comparison"
},
"empty": {
"no_months": "No months to display",
"no_departments": "No departments for this month",
"no_customers": "No customers match the current filters",
"no_compare_results": "No comparison results yet",
"no_comparison_months": "No month available",
"no_month_comparison": "Select a comparison month to view deltas."
},
"loading": {
"loading_months": "Loading months ({loaded}/{total})"
},
"compare": {
"compare_month": "Compare against month",
"month_to_month": "Month-over-month comparison",
"mode": "Comparison mode",
"run": "Compare all local invoices with E-conomic",
"line_mode_disclaimer": "Line-by-line mode uses available compare metadata and warning lines from the API response.",
"total_checked": "Invoices checked",
"mismatches": "Mismatches",
"warnings": "Warnings",
"total_difference": "Total difference",
"modes": {
"invoice_total": "Invoice total only",
"line_by_line": "Line-by-line summary"
},
"status": {
"ok": "Match",
"mismatch": "Mismatch"
},
"run_short": "Run compare",
"progress": "Processing {processed} / {total}",
"sorted_hint": "Rows are sorted by mismatches and warning severity first.",
"warning_details": "Warnings for invoice #{invoiceId}"
},
"sections": {
"trend": "Monthly trend",
"month_table": "Month totals",
"source_blend": "Source blend",
"top_months": "Top months by total"
},
"departments": {
"allocation_hint": "Booked allocation is estimated from each department's distribution share.",
"rows": "departments"
},
"aria": {
"trend_chart": "Distribution trend chart by month",
"month_table_caption": "Monthly distribution totals with source mix and quick-open actions",
"sort_month": "Sort table by month",
"sort_total": "Sort table by total amount",
"source_mix_row": "Source composition for {month}",
"open_month": "Open distribution details for {month}",
"month_comparison_caption": "Month-over-month comparison table for key metrics",
"department_table_caption": "Department-level allocation split for the selected month",
"customer_table_caption": "Customer-level allocation rows for the selected month",
"compare_table_caption": "Invoice comparison results sorted by mismatches and warnings"
}
}
}
+146
View File
@@ -3910,6 +3910,152 @@
"enter_code": "Enter your verification code",
"back_to_login": "Back to Login"
},
"superuser_invoice_distribution": {
"tab_label": "Distribution",
"overview": {
"title": "Departmental Distribution",
"subtitle": "Monthly overview of shared income allocation by source.",
"loaded_months": "{loaded} months loaded"
},
"monthly_page": {
"title": "Distribution for {month}",
"subtitle": "Review departmental allocations, customer split, and invoice comparisons.",
"toolbar_month": "Month selector",
"toolbar_month_picker": "Select month"
},
"actions": {
"reload": "Reload",
"open_month": "Open month",
"try_again": "Try again",
"reset_filters": "Reset filters",
"previous_month": "Go to previous month",
"next_month": "Go to next month",
"close": "Close"
},
"filters": {
"search": "Search month",
"search_placeholder": "Find month...",
"year": "Year",
"all_years": "All years",
"search_department": "Search department",
"search_department_placeholder": "Department name...",
"search_customer": "Search customer",
"search_customer_placeholder": "Customer name or number...",
"source": "Source",
"all_sources": "All sources",
"department": "Department",
"all_departments": "All departments"
},
"metrics": {
"total": "Total",
"booked": "Booked invoices",
"distribution": "Departmental distribution",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Vehicle subscriptions",
"other": "Other booked income",
"months": "Months in view"
},
"tabs": {
"overview": "Overview",
"departments": "Departments",
"customers": "Customers",
"compare": "Compare"
},
"table": {
"month": "Month",
"actions": "Actions",
"metric": "Metric",
"current": "Current",
"compared": "Compared month",
"delta": "Delta",
"department": "Department",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Subscriptions",
"total": "Total",
"share": "Share",
"estimated_booked_allocation": "Estimated booked allocation",
"customer": "Customer",
"source": "Source",
"amount": "Amount",
"department_allocations": "Department allocations",
"invoice_id": "Invoice ID",
"internal_total": "Local total",
"economic_total": "E-conomic total",
"warnings": "Warnings",
"status": "Status",
"booked": "Booked",
"distribution": "Distribution",
"source_mix": "Source mix"
},
"tooltips": {
"summary": "Each month includes booked totals plus fixed-pricing and subscription distribution totals.",
"fixed_pricing": "Amount assigned from fixed-price agreements for the selected month.",
"subscriptions": "Amount assigned from vehicle subscriptions for the selected month.",
"estimated_booked": "Estimated booked share based on each department's proportion of the shared distribution.",
"compare_mode": "Invoice total compares aggregate totals. Line-by-line provides per-invoice line summary from available compare data."
},
"errors": {
"overview_load_failed": "Could not load monthly distribution overview",
"month_load_failed": "Could not load selected month",
"compare_failed": "Could not complete comparison"
},
"empty": {
"no_months": "No months to display",
"no_departments": "No departments for this month",
"no_customers": "No customers match the current filters",
"no_compare_results": "No comparison results yet",
"no_comparison_months": "No month available",
"no_month_comparison": "Select a comparison month to view deltas."
},
"loading": {
"loading_months": "Loading months ({loaded}/{total})"
},
"compare": {
"compare_month": "Compare against month",
"month_to_month": "Month-over-month comparison",
"mode": "Comparison mode",
"run": "Compare all local invoices with E-conomic",
"line_mode_disclaimer": "Line-by-line mode uses available compare metadata and warning lines from the API response.",
"total_checked": "Invoices checked",
"mismatches": "Mismatches",
"warnings": "Warnings",
"total_difference": "Total difference",
"modes": {
"invoice_total": "Invoice total only",
"line_by_line": "Line-by-line summary"
},
"status": {
"ok": "Match",
"mismatch": "Mismatch"
},
"run_short": "Run compare",
"progress": "Processing {processed} / {total}",
"sorted_hint": "Rows are sorted by mismatches and warning severity first.",
"warning_details": "Warnings for invoice #{invoiceId}"
},
"sections": {
"trend": "Monthly trend",
"month_table": "Month totals",
"source_blend": "Source blend",
"top_months": "Top months by total"
},
"departments": {
"allocation_hint": "Booked allocation is estimated from each department's distribution share.",
"rows": "departments"
},
"aria": {
"trend_chart": "Distribution trend chart by month",
"month_table_caption": "Monthly distribution totals with source mix and quick-open actions",
"sort_month": "Sort table by month",
"sort_total": "Sort table by total amount",
"source_mix_row": "Source composition for {month}",
"open_month": "Open distribution details for {month}",
"month_comparison_caption": "Month-over-month comparison table for key metrics",
"department_table_caption": "Department-level allocation split for the selected month",
"customer_table_caption": "Customer-level allocation rows for the selected month",
"compare_table_caption": "Invoice comparison results sorted by mismatches and warnings"
}
},
"connectivity": {
"title": "Connectivity Issue",
"subtitle": "Retrying every 5 seconds...",
+146
View File
@@ -3919,5 +3919,151 @@
"disable_error_title": "Kunne ikke deaktivere",
"enter_code": "Skriv inn verifiseringskoden din",
"back_to_login": "Tilbake til innlogging"
},
"superuser_invoice_distribution": {
"tab_label": "Distribution",
"overview": {
"title": "Departmental Distribution",
"subtitle": "Monthly overview of shared income allocation by source.",
"loaded_months": "{loaded} months loaded"
},
"monthly_page": {
"title": "Distribution for {month}",
"subtitle": "Review departmental allocations, customer split, and invoice comparisons.",
"toolbar_month": "Month selector",
"toolbar_month_picker": "Select month"
},
"actions": {
"reload": "Reload",
"open_month": "Open month",
"try_again": "Try again",
"reset_filters": "Reset filters",
"previous_month": "Go to previous month",
"next_month": "Go to next month",
"close": "Close"
},
"filters": {
"search": "Search month",
"search_placeholder": "Find month...",
"year": "Year",
"all_years": "All years",
"search_department": "Search department",
"search_department_placeholder": "Department name...",
"search_customer": "Search customer",
"search_customer_placeholder": "Customer name or number...",
"source": "Source",
"all_sources": "All sources",
"department": "Department",
"all_departments": "All departments"
},
"metrics": {
"total": "Total",
"booked": "Booked invoices",
"distribution": "Departmental distribution",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Vehicle subscriptions",
"other": "Other booked income",
"months": "Months in view"
},
"tabs": {
"overview": "Overview",
"departments": "Departments",
"customers": "Customers",
"compare": "Compare"
},
"table": {
"month": "Month",
"actions": "Actions",
"metric": "Metric",
"current": "Current",
"compared": "Compared month",
"delta": "Delta",
"department": "Department",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Subscriptions",
"total": "Total",
"share": "Share",
"estimated_booked_allocation": "Estimated booked allocation",
"customer": "Customer",
"source": "Source",
"amount": "Amount",
"department_allocations": "Department allocations",
"invoice_id": "Invoice ID",
"internal_total": "Local total",
"economic_total": "E-conomic total",
"warnings": "Warnings",
"status": "Status",
"booked": "Booked",
"distribution": "Distribution",
"source_mix": "Source mix"
},
"tooltips": {
"summary": "Each month includes booked totals plus fixed-pricing and subscription distribution totals.",
"fixed_pricing": "Amount assigned from fixed-price agreements for the selected month.",
"subscriptions": "Amount assigned from vehicle subscriptions for the selected month.",
"estimated_booked": "Estimated booked share based on each department's proportion of the shared distribution.",
"compare_mode": "Invoice total compares aggregate totals. Line-by-line provides per-invoice line summary from available compare data."
},
"errors": {
"overview_load_failed": "Could not load monthly distribution overview",
"month_load_failed": "Could not load selected month",
"compare_failed": "Could not complete comparison"
},
"empty": {
"no_months": "No months to display",
"no_departments": "No departments for this month",
"no_customers": "No customers match the current filters",
"no_compare_results": "No comparison results yet",
"no_comparison_months": "No month available",
"no_month_comparison": "Select a comparison month to view deltas."
},
"loading": {
"loading_months": "Loading months ({loaded}/{total})"
},
"compare": {
"compare_month": "Compare against month",
"month_to_month": "Month-over-month comparison",
"mode": "Comparison mode",
"run": "Compare all local invoices with E-conomic",
"line_mode_disclaimer": "Line-by-line mode uses available compare metadata and warning lines from the API response.",
"total_checked": "Invoices checked",
"mismatches": "Mismatches",
"warnings": "Warnings",
"total_difference": "Total difference",
"modes": {
"invoice_total": "Invoice total only",
"line_by_line": "Line-by-line summary"
},
"status": {
"ok": "Match",
"mismatch": "Mismatch"
},
"run_short": "Run compare",
"progress": "Processing {processed} / {total}",
"sorted_hint": "Rows are sorted by mismatches and warning severity first.",
"warning_details": "Warnings for invoice #{invoiceId}"
},
"sections": {
"trend": "Monthly trend",
"month_table": "Month totals",
"source_blend": "Source blend",
"top_months": "Top months by total"
},
"departments": {
"allocation_hint": "Booked allocation is estimated from each department's distribution share.",
"rows": "departments"
},
"aria": {
"trend_chart": "Distribution trend chart by month",
"month_table_caption": "Monthly distribution totals with source mix and quick-open actions",
"sort_month": "Sort table by month",
"sort_total": "Sort table by total amount",
"source_mix_row": "Source composition for {month}",
"open_month": "Open distribution details for {month}",
"month_comparison_caption": "Month-over-month comparison table for key metrics",
"department_table_caption": "Department-level allocation split for the selected month",
"customer_table_caption": "Customer-level allocation rows for the selected month",
"compare_table_caption": "Invoice comparison results sorted by mismatches and warnings"
}
}
}
+146
View File
@@ -3919,5 +3919,151 @@
"disable_error_title": "Kunde inte inaktivera",
"enter_code": "Ange din verifieringskod",
"back_to_login": "Tillbaka till inloggning"
},
"superuser_invoice_distribution": {
"tab_label": "Distribution",
"overview": {
"title": "Departmental Distribution",
"subtitle": "Monthly overview of shared income allocation by source.",
"loaded_months": "{loaded} months loaded"
},
"monthly_page": {
"title": "Distribution for {month}",
"subtitle": "Review departmental allocations, customer split, and invoice comparisons.",
"toolbar_month": "Month selector",
"toolbar_month_picker": "Select month"
},
"actions": {
"reload": "Reload",
"open_month": "Open month",
"try_again": "Try again",
"reset_filters": "Reset filters",
"previous_month": "Go to previous month",
"next_month": "Go to next month",
"close": "Close"
},
"filters": {
"search": "Search month",
"search_placeholder": "Find month...",
"year": "Year",
"all_years": "All years",
"search_department": "Search department",
"search_department_placeholder": "Department name...",
"search_customer": "Search customer",
"search_customer_placeholder": "Customer name or number...",
"source": "Source",
"all_sources": "All sources",
"department": "Department",
"all_departments": "All departments"
},
"metrics": {
"total": "Total",
"booked": "Booked invoices",
"distribution": "Departmental distribution",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Vehicle subscriptions",
"other": "Other booked income",
"months": "Months in view"
},
"tabs": {
"overview": "Overview",
"departments": "Departments",
"customers": "Customers",
"compare": "Compare"
},
"table": {
"month": "Month",
"actions": "Actions",
"metric": "Metric",
"current": "Current",
"compared": "Compared month",
"delta": "Delta",
"department": "Department",
"fixed_pricing": "Fixed pricing",
"subscriptions": "Subscriptions",
"total": "Total",
"share": "Share",
"estimated_booked_allocation": "Estimated booked allocation",
"customer": "Customer",
"source": "Source",
"amount": "Amount",
"department_allocations": "Department allocations",
"invoice_id": "Invoice ID",
"internal_total": "Local total",
"economic_total": "E-conomic total",
"warnings": "Warnings",
"status": "Status",
"booked": "Booked",
"distribution": "Distribution",
"source_mix": "Source mix"
},
"tooltips": {
"summary": "Each month includes booked totals plus fixed-pricing and subscription distribution totals.",
"fixed_pricing": "Amount assigned from fixed-price agreements for the selected month.",
"subscriptions": "Amount assigned from vehicle subscriptions for the selected month.",
"estimated_booked": "Estimated booked share based on each department's proportion of the shared distribution.",
"compare_mode": "Invoice total compares aggregate totals. Line-by-line provides per-invoice line summary from available compare data."
},
"errors": {
"overview_load_failed": "Could not load monthly distribution overview",
"month_load_failed": "Could not load selected month",
"compare_failed": "Could not complete comparison"
},
"empty": {
"no_months": "No months to display",
"no_departments": "No departments for this month",
"no_customers": "No customers match the current filters",
"no_compare_results": "No comparison results yet",
"no_comparison_months": "No month available",
"no_month_comparison": "Select a comparison month to view deltas."
},
"loading": {
"loading_months": "Loading months ({loaded}/{total})"
},
"compare": {
"compare_month": "Compare against month",
"month_to_month": "Month-over-month comparison",
"mode": "Comparison mode",
"run": "Compare all local invoices with E-conomic",
"line_mode_disclaimer": "Line-by-line mode uses available compare metadata and warning lines from the API response.",
"total_checked": "Invoices checked",
"mismatches": "Mismatches",
"warnings": "Warnings",
"total_difference": "Total difference",
"modes": {
"invoice_total": "Invoice total only",
"line_by_line": "Line-by-line summary"
},
"status": {
"ok": "Match",
"mismatch": "Mismatch"
},
"run_short": "Run compare",
"progress": "Processing {processed} / {total}",
"sorted_hint": "Rows are sorted by mismatches and warning severity first.",
"warning_details": "Warnings for invoice #{invoiceId}"
},
"sections": {
"trend": "Monthly trend",
"month_table": "Month totals",
"source_blend": "Source blend",
"top_months": "Top months by total"
},
"departments": {
"allocation_hint": "Booked allocation is estimated from each department's distribution share.",
"rows": "departments"
},
"aria": {
"trend_chart": "Distribution trend chart by month",
"month_table_caption": "Monthly distribution totals with source mix and quick-open actions",
"sort_month": "Sort table by month",
"sort_total": "Sort table by total amount",
"source_mix_row": "Source composition for {month}",
"open_month": "Open distribution details for {month}",
"month_comparison_caption": "Month-over-month comparison table for key metrics",
"department_table_caption": "Department-level allocation split for the selected month",
"customer_table_caption": "Customer-level allocation rows for the selected month",
"compare_table_caption": "Invoice comparison results sorted by mismatches and warnings"
}
}
}
+14
View File
@@ -79,6 +79,8 @@ import DepartmentStripeSetup from "@/views/dashboards/superUserDashboard/departm
import CollectedOrderInvoices from "@/views/dashboards/superUserDashboard/CollectedOrderInvoices.vue";
import CollectedOrderInvoice
from "@/views/dashboards/superUserDashboard/collectedOrderInvoice/collectedOrderInvoice.vue";
import InvoiceDistributionMonthView
from "@/views/dashboards/superUserDashboard/invoiceDistribution/InvoiceDistributionMonthView.vue";
import DepartmentDailyReport
from "@/views/dashboards/departmentDashboard/modules/daily-report/DepartmentDailyReport.vue";
import SuperUserDashboardProduct from "@/views/dashboards/superUserDashboard/products/SuperUserDashboardProduct.vue";
@@ -689,6 +691,18 @@ export const router = createRouter({
component: CollectedOrderInvoices,
meta: { middleware: superUserMiddleware }
},
{
name: 'collectedorderinvoicesdistribution',
path: '/superuser/invoices/distribution/:year/:month',
component: InvoiceDistributionMonthView,
meta: { middleware: superUserMiddleware }
},
{
name: 'collectedorderinvoicesdistributiontab',
path: '/superuser/invoices/distribution/:year/:month/:tab',
component: InvoiceDistributionMonthView,
meta: { middleware: superUserMiddleware }
},
{
name: 'collectedorderinvoice',
path: '/superuser/invoices/:collectedOrderInvoiceId',
@@ -12,39 +12,40 @@ import InvoiceOrdersPagination
import {useRouter} from "vue-router";
import InvoicingBillingPeriod
from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/InvoicingBillingPeriod.vue";
import InvoiceDistributionOverview
from "@/views/dashboards/superUserDashboard/invoiceDistribution/InvoiceDistributionOverview.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const tabs = ref([
{ name: 'Overblik', slot: 'overview', icon: 'chart-pie', hidden: false },
{ name: 'Fakturaer', slot: 'invoices', icon: 'list', hidden: false },
//{ name: SessionUser.objects.orders.meta.title, slot: 'transactions', icon: 'fas fa-shopping-cart', hidden: false },
{ name: 'Periode', slot: 'period', icon: 'calendar-alt', hidden: false },
//{ name: 'Faktura pr. måned', slot: 'monthly', icon: 'fas fa-list', hidden: false },
//{ name: 'Faktura pr. ordre', slot: 'orderly', icon: 'fas fa-check', hidden: false }
{ name: t('superuser_invoice_distribution.tab_label'), slot: 'distribution', icon: 'sitemap', hidden: false },
])
const router = useRouter();
// Initialize the active tab
const activeTab = ref('overview');
// Watch for changes in the route to set the query parameter
watch(() => router.currentRoute.value.query.activeTab, (newTab) => {
if (newTab && tabs.value.some(tab => tab.slot === newTab)) {
activeTab.value = newTab;
}
});
// Watch for changes in the active tab and update the query parameter
watch(activeTab, (newTab) => {
if (newTab && router.currentRoute.value.query.activeTab !== newTab) {
router.replace({ query: { ...router.currentRoute.value.query, activeTab: newTab } });
}
});
// Set the initial active tab based on the query parameter
onMounted(() => {
const queryTab = router.currentRoute.value.query.activeTab;
if (queryTab && tabs.value.some(tab => tab.slot === queryTab)) {
activeTab.value = queryTab;
} else {
activeTab.value = 'overview'; // Default to overview if no valid tab is found
activeTab.value = 'overview';
}
});
@@ -58,10 +59,8 @@ function getTabComponent(tabName) {
return InvoiceOrdersPagination;
case 'period':
return InvoicingBillingPeriod;
//case 'monthly':
// return MonthlyInvoiceStatistics;
//case 'orderly':
// return OrderlyInvoiceStatistics;
case 'distribution':
return InvoiceDistributionOverview;
default:
return null;
}
+187 -21
View File
@@ -1,4 +1,4 @@
const API_HOST = "**://api.truckwash.io/**";
const API_HOST = /https?:\/\/(?:api\.truckwash\.io|localhost|127\.0\.0\.1)(?::\d+)?\/api\/.*/i;
function json(body, status = 200) {
return {
@@ -12,6 +12,8 @@ export async function mockApi(page, options = {}) {
await page.route(API_HOST, async (route) => {
const request = route.request();
const url = request.url();
const parsedUrl = new URL(url);
const pathname = parsedUrl.pathname;
const method = request.method();
if (url.includes("/auth/recaptcha/pre-check") && method === "GET") {
@@ -52,33 +54,197 @@ export async function mockApi(page, options = {}) {
return;
}
const defaultSession = {
id: 1,
customer_number: 12345,
group_id: 1,
email: "e2e@example.com",
phone: {
number: "12345678",
country_code: 45
},
notifications: {
wash_certificate_email: null,
email_notifications_enabled: true,
sms_notifications_enabled: false
},
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
display_name: "E2E User",
permissions: options.permissions || ["user"],
economic_customer: []
};
const sessionData = {
...defaultSession,
...(options.sessionData || {})
};
if (options.permissions) {
sessionData.permissions = options.permissions;
}
await route.fulfill(
json({
data: {
id: 1,
customer_number: 12345,
group_id: 1,
email: "e2e@example.com",
phone: {
number: "12345678",
country_code: 45
},
notifications: {
wash_certificate_email: null,
email_notifications_enabled: true,
sms_notifications_enabled: false
},
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
display_name: "E2E User",
permissions: ["user"],
economic_customer: []
}
data: sessionData
})
);
return;
}
if (options.invoiceDistribution) {
const monthFromDate = parsedUrl.searchParams.get("dateFrom");
const monthNumber = monthFromDate ? Number(monthFromDate.split("-")[1]) : 1;
const monthBase = Number.isFinite(monthNumber) ? monthNumber * 10 : 10;
if (pathname.endsWith("/orders") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 1,
created_at: "2026-01-01T00:00:00.000Z"
}
]
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period") && method === "GET") {
await route.fulfill(
json({
data: {
types: {
all: [
{
transactions: [
{ amount: 100 + monthBase, booked: true, excluded: false },
{ amount: 50 + monthBase, booked: true, excluded: false },
{ amount: 25, booked: false, excluded: false }
]
}
]
}
}
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/fixed-pricing") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 10,
customer_number: 1001,
customer_name: "Acme Transport",
meta: {
fixed_pricing: {
created_at: "2026-01-10T00:00:00.000Z",
price: 80 + monthBase,
original_price: 120 + monthBase,
department_totals_relative: { 1: 40 + monthBase, 2: 40 }
}
}
}
],
includes: {
collective_fixed_pricing_results: {
total_fixed_price: 80 + monthBase,
total_department_totals_relative_parsed: {
Copenhagen: 40 + monthBase,
Odense: 40
}
}
}
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/wash-subscriptions") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 11,
customer_number: 1002,
customer_name: "Nordic Haul",
meta: {
wash_subscription: {
created_at: "2026-01-05T00:00:00.000Z",
price: 40 + monthBase,
original_price: 55 + monthBase,
department_totals_relative: { 1: 20 + monthBase, 2: 20 }
}
}
}
],
includes: {
collective_subscription_results: {
total_subscription_price: 40 + monthBase,
subscription_price_department_distribution_parsed: {
Copenhagen: 20 + monthBase,
Odense: 20
}
}
}
})
);
return;
}
if (pathname.endsWith("/departments") && method === "GET") {
await route.fulfill(
json({
data: [
{ id: 1, name: "Copenhagen" },
{ id: 2, name: "Odense" }
]
})
);
return;
}
if (pathname.endsWith("/collected-invoices") && method === "GET") {
const invoices = [101, 102, 103, 104, 105, 106].map((invoiceId, index) => ({
id: invoiceId,
customer_number: 1001 + index,
customer_name: `Customer ${invoiceId}`,
total_net_amount: 100 + (index * 10)
}));
await route.fulfill(
json({
data: invoices
})
);
return;
}
if (pathname.endsWith("/collected-invoices/economic/compare") && method === "GET") {
const invoiceId = Number(parsedUrl.searchParams.get("collected_invoice_id") || 0);
const mismatch = invoiceId === 101 || invoiceId === 103;
await new Promise((resolve) => setTimeout(resolve, 350));
await route.fulfill(
json({
data: {
collected_invoice_id: invoiceId,
warnings: mismatch ? ["Line mismatch found"] : [],
internal_total: mismatch ? 150 : 120,
booked_total: mismatch ? 145 : 120,
draft_total: null,
difference: mismatch ? 5 : 0,
order_ids: mismatch ? [1, 2] : [3]
}
})
);
return;
}
}
if (options.fallbackPassthrough) {
await route.fallback();
return;
@@ -108,6 +108,14 @@ describe("superuser invoices route wiring", () => {
expect(routerSource).toContain("component: CollectedOrderInvoices");
});
it("maps monthly distribution routes to InvoiceDistributionMonthView", () => {
expect(routerSource).toContain("name: 'collectedorderinvoicesdistribution'");
expect(routerSource).toContain("path: '/superuser/invoices/distribution/:year/:month'");
expect(routerSource).toContain("name: 'collectedorderinvoicesdistributiontab'");
expect(routerSource).toContain("path: '/superuser/invoices/distribution/:year/:month/:tab'");
expect(routerSource).toContain("component: InvoiceDistributionMonthView");
});
it("keeps details route for a single collected invoice", () => {
expect(routerSource).toContain("name: 'collectedorderinvoice'");
expect(routerSource).toContain("path: '/superuser/invoices/:collectedOrderInvoiceId'");
@@ -120,6 +128,7 @@ describe("CollectedOrderInvoices tab container contract", () => {
expect(invoicesViewSource).toContain("{ name: 'Overblik', slot: 'overview'");
expect(invoicesViewSource).toContain("{ name: 'Fakturaer', slot: 'invoices'");
expect(invoicesViewSource).toContain("{ name: 'Periode', slot: 'period'");
expect(invoicesViewSource).toContain("slot: 'distribution'");
});
it("keeps active tab synced with route query", () => {
@@ -141,6 +150,8 @@ describe("CollectedOrderInvoices tab container contract", () => {
expect(invoicesViewSource).toContain("return CollectedOrderInvoicesListPagination;");
expect(invoicesViewSource).toContain("case 'period':");
expect(invoicesViewSource).toContain("return InvoicingBillingPeriod;");
expect(invoicesViewSource).toContain("case 'distribution':");
expect(invoicesViewSource).toContain("return InvoiceDistributionOverview;");
});
});