fix(invoicing-period): propagate flagged wash start date to Selvvask view (#304)
Fixes AUT-6 (TRU-10). ## Problem When a user clicked an `xlvask_usage_log` token from a flag in the InvoicingPeriod flag list, `openXlVaskUsageLog(flag)` navigated to: ``` /invoices?activeTab=period&periodView=self_wash&xlvaskUsageLogId=<id> ``` `InvoicingBillingPeriodViewSelfWash.vue` already read `route.query.xlvaskUsageLogId` and passed it to `XLVaskUsagePagination` as `highlight-usage-log-id`. However, the date range was hardcoded to `dates.computed.formattedStartDate.value` / `dates.computed.formattedEndDate.value` — the parent period view's range. If the flag was emitted for a wash **outside** that range (e.g. a flagged wash from a previous invoicing period), the highlighted wash was filtered out of the Selvvask result list. ## Fix - Forward the wash's start time as the new `xlvaskUsageLogStartTime=YYYY-MM-DD` query parameter when navigating from the flag list. The `extractDateOnly` helper in `InvoicingPeriodFlagList.vue` accepts both `flag.context.start_time` and `flag.start_time`, and tolerates the full ISO-8601 form (e.g. `2026-04-28T08:15:00`). - `InvoicingBillingPeriodViewSelfWash.vue` reads `route.query.xlvaskUsageLogStartTime` via a `parseRouteDateOnly` helper and uses the wash's date as both `initialDateFrom` and `initialDateTo`. When the parameter is absent (e.g. direct navigation to Selvvask), the view falls back to the parent period's range, preserving existing behaviour. ## Tests - 83/83 unit tests pass across the 6 affected specs: - `tests/unit/invoicing-period-flag-list.spec.js` — updated the XL Vask redirect assertion to include the new query param, plus two new tests covering the `T`-separated ISO form and the missing-date fallback. - `tests/unit/superuser-invoices-view.spec.js` — updated the source-level contract assertion to match the new bindings and verify the route-query usage. - `tests/unit/invoicing-period-flag-badge.spec.js`, `invoicing-period-flag-badge-buefy.spec.js`, `invoicing-period-view-totals.spec.js`, `order-content-table-flags.spec.js` — all unchanged, still green. - ESLint clean on all four modified files. - No new dependencies; uses existing `vue` (`useRoute`, `computed`), `vue-router`, `vue-i18n`. ## Files changed (4 files, +87 / -5) - `src/views/dashboards/superUserDashboard/InvoicingBillingPeriod/components/InvoicingPeriodFlagList.vue` (+14) - `src/views/dashboards/superUserDashboard/InvoicingBillingPeriod/views/InvoicingBillingPeriodViewSelfWash.vue` (+18 / -2) - `tests/unit/invoicing-period-flag-list.spec.js` (+50 / -1) - `tests/unit/superuser-invoices-view.spec.js` (+5 / -2) Logic-only frontend filter adjustment; no visual change (so no before/after screenshots required) and no backend API endpoint touched. _This PR was created by an AI agent (OpenHands) on behalf of the OpenSymphony autonomous workflow._ Co-authored-by: Jeppe <jeppe@copenhagentruckwash.io> Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
co-authored by
Jeppe
openhands
parent
1e7298245d
commit
f0b3fc4675
+14
@@ -285,8 +285,18 @@ const openOrderItem = (flag: any) => {
|
||||
SessionUser.functions.redirectTo.department(departmentId, `modules/pos/orders/${orderId}${suffix}`, true);
|
||||
};
|
||||
|
||||
const extractDateOnly = (value: any) => {
|
||||
const rawValue = String(value ?? "").trim();
|
||||
if (rawValue === "") {
|
||||
return "";
|
||||
}
|
||||
const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(rawValue);
|
||||
return match ? `${match[1]}-${match[2]}-${match[3]}` : "";
|
||||
};
|
||||
|
||||
const openXlVaskUsageLog = (flag: any) => {
|
||||
const usageLogId = Number(flag?.xlvask_usage_log_id || flag?.context?.xlvask_usage_log_id || flag?.target_id || 0);
|
||||
const startTime = extractDateOnly(flag?.context?.start_time || flag?.start_time);
|
||||
const query = new URLSearchParams({
|
||||
activeTab: "period",
|
||||
periodView: "self_wash",
|
||||
@@ -296,6 +306,10 @@ const openXlVaskUsageLog = (flag: any) => {
|
||||
query.set("xlvaskUsageLogId", String(usageLogId));
|
||||
}
|
||||
|
||||
if (startTime !== "") {
|
||||
query.set("xlvaskUsageLogStartTime", startTime);
|
||||
}
|
||||
|
||||
SessionUser.functions.redirectTo.superUser(`/invoices?${query.toString()}`, true);
|
||||
};
|
||||
|
||||
|
||||
+18
-2
@@ -5,12 +5,28 @@ import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
const DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
|
||||
const parseRouteDateOnly = (value: any) => {
|
||||
const rawValue = Array.isArray(value) ? String(value[0] ?? "") : String(value ?? "");
|
||||
const match = DATE_ONLY_PATTERN.exec(rawValue.trim());
|
||||
if (!match) {
|
||||
return "";
|
||||
}
|
||||
return `${match[1]}-${match[2]}-${match[3]}`;
|
||||
};
|
||||
|
||||
const route = useRoute();
|
||||
const highlightedUsageLogId = computed(() => {
|
||||
const parsedId = Number.parseInt(String(route.query.xlvaskUsageLogId || ""), 10);
|
||||
return Number.isInteger(parsedId) && parsedId > 0 ? parsedId : 0;
|
||||
});
|
||||
|
||||
const flaggedWashStartDate = computed(() => parseRouteDateOnly(route.query.xlvaskUsageLogStartTime));
|
||||
|
||||
const initialDateFrom = computed(() => flaggedWashStartDate.value || dates.computed.formattedStartDate.value);
|
||||
const initialDateTo = computed(() => flaggedWashStartDate.value || dates.computed.formattedEndDate.value);
|
||||
|
||||
const selfWashTitle = computed(() => i18n.global.t("nav.self_wash"));
|
||||
</script>
|
||||
|
||||
@@ -18,8 +34,8 @@ const selfWashTitle = computed(() => i18n.global.t("nav.self_wash"));
|
||||
<section data-testid="invoicing-period-self-wash-view">
|
||||
<XLVaskUsagePagination
|
||||
:title="selfWashTitle"
|
||||
:initial-date-from="dates.computed.formattedStartDate.value"
|
||||
:initial-date-to="dates.computed.formattedEndDate.value"
|
||||
:initial-date-from="initialDateFrom"
|
||||
:initial-date-to="initialDateTo"
|
||||
:inherit-period-filters="true"
|
||||
:load-all-at-once="false"
|
||||
:highlight-usage-log-id="highlightedUsageLogId"
|
||||
|
||||
@@ -390,7 +390,56 @@ describe("InvoicingPeriodFlagList", () => {
|
||||
|
||||
await token.trigger("click");
|
||||
expect(SessionUser.functions.redirectTo.superUser).toHaveBeenCalledWith(
|
||||
"/invoices?activeTab=period&periodView=self_wash&xlvaskUsageLogId=55",
|
||||
"/invoices?activeTab=period&periodView=self_wash&xlvaskUsageLogId=55&xlvaskUsageLogStartTime=2026-05-11",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("propagates the flagged wash start time when navigating to the Selvvask view", async () => {
|
||||
const wrapper = mountList([
|
||||
{
|
||||
id: "auto-xlvask-root",
|
||||
source: "automatic",
|
||||
fingerprint: "xlvask-root",
|
||||
definition_key: "xlvask_missing_order_link",
|
||||
message_key: "invoice_period.flags.automatic.xlvask_missing_order_link",
|
||||
target_type: "xlvask_usage_log",
|
||||
target_id: 77,
|
||||
xlvask_usage_log_id: 77,
|
||||
context: {
|
||||
xlvask_usage_log_id: 77,
|
||||
start_time: "2026-04-28T08:15:00",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await wrapper.get(".invoice-period-flag-token").trigger("click");
|
||||
expect(SessionUser.functions.redirectTo.superUser).toHaveBeenLastCalledWith(
|
||||
"/invoices?activeTab=period&periodView=self_wash&xlvaskUsageLogId=77&xlvaskUsageLogStartTime=2026-04-28",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("navigates to Selvvask without a start-time query when the flag has no wash date", async () => {
|
||||
const wrapper = mountList([
|
||||
{
|
||||
id: "auto-xlvask-no-date",
|
||||
source: "automatic",
|
||||
fingerprint: "xlvask-no-date",
|
||||
definition_key: "xlvask_missing_order_link",
|
||||
message_key: "invoice_period.flags.automatic.xlvask_missing_order_link",
|
||||
target_type: "xlvask_usage_log",
|
||||
target_id: 91,
|
||||
xlvask_usage_log_id: 91,
|
||||
context: {
|
||||
xlvask_usage_log_id: 91,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await wrapper.get(".invoice-period-flag-token").trigger("click");
|
||||
expect(SessionUser.functions.redirectTo.superUser).toHaveBeenLastCalledWith(
|
||||
"/invoices?activeTab=period&periodView=self_wash&xlvaskUsageLogId=91",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
@@ -577,8 +577,11 @@ describe("Periode tab contract", () => {
|
||||
});
|
||||
|
||||
it("uses the selected period while keeping Selvvask results server-paginated", () => {
|
||||
expect(periodViewSelfWashSource).toContain(':initial-date-from="dates.computed.formattedStartDate.value"');
|
||||
expect(periodViewSelfWashSource).toContain(':initial-date-to="dates.computed.formattedEndDate.value"');
|
||||
expect(periodViewSelfWashSource).toContain(':initial-date-from="initialDateFrom"');
|
||||
expect(periodViewSelfWashSource).toContain(':initial-date-to="initialDateTo"');
|
||||
expect(periodViewSelfWashSource).toContain("dates.computed.formattedStartDate.value");
|
||||
expect(periodViewSelfWashSource).toContain("dates.computed.formattedEndDate.value");
|
||||
expect(periodViewSelfWashSource).toContain("route.query.xlvaskUsageLogStartTime");
|
||||
expect(periodViewSelfWashSource).toContain(':inherit-period-filters="true"');
|
||||
expect(periodViewSelfWashSource).toContain(':load-all-at-once="false"');
|
||||
expect(xlvaskUsagePaginationSource).toContain("inheritPeriodFilters");
|
||||
|
||||
Reference in New Issue
Block a user