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:
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user