fix(workorders): keep closed undated WOs out of the board page like the search (SH-391)

With no range selected, or with Unscheduled, the board search adds only
open undated work orders; completed and canceled ones without a date are
never unscheduled. The board page kept them. It now applies the same rule,
so both return the same rows.
This commit is contained in:
Alexandre Brandizzi 2026-09-24 21:34:24 -03:00
parent f69c8a04eb
commit 51ca244524
2 changed files with 38 additions and 9 deletions

View file

@ -2,6 +2,11 @@ import type { WorkOrderAdvancedFilters } from "@/domain/work-orders/types/work-o
import { ASSIGNEE_FILTER_UNASSIGNED } from "@/domain/work-orders/types/work-order-list";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
import { dateRangeFor } from "@/domain/work-orders/utils/advanced-filters-utils";
import {
ADVANCED_SEARCH_ALL_WEEKS_FROM,
ADVANCED_SEARCH_ALL_WEEKS_TO,
} from "@/domain/work-orders/utils/board-query-params";
import { isTerminalCarryOverStatus } from "@/domain/work-orders/utils/carry-over-display";
import { addDaysIso } from "@/domain/work-orders/utils/wizard-date-utils";
type AdvancedFilterPredicate = (
@ -9,22 +14,32 @@ type AdvancedFilterPredicate = (
filters: WorkOrderAdvancedFilters,
) => boolean;
const isOpenUndated = (row: WorkOrderTableRow): boolean =>
!row.scheduledOn && !isTerminalCarryOverStatus(row.status);
// Mirrors the board search (ApplyDateRangeFilter and ApplyUnscheduledOnlyFilter)
// so the page and /board/search keep the same rows (SH-391): a row is in a range
// when its date is, or when it is week-only and its target week overlaps. With
// no range, every week is searched and open undated rows are added. Closed
// undated rows are never unscheduled.
const matchesDateRange: AdvancedFilterPredicate = (row, filters) => {
if (filters.unscheduledOnly) {
return !row.scheduledOn;
return isOpenUndated(row);
}
const range = dateRangeFor(filters);
if (!range) return true;
const [from, to] = range;
const [from, to] = dateRangeFor(filters) ?? [
ADVANCED_SEARCH_ALL_WEEKS_FROM,
ADVANCED_SEARCH_ALL_WEEKS_TO,
];
if (row.scheduledOn && row.scheduledOn >= from && row.scheduledOn <= to) return true;
// Week-only rows also match when their target week overlaps the range, the
// same rule as the board search (SH-391).
return (
if (
row.scheduleWeekOnly &&
Boolean(row.targetWeek) &&
row.targetWeek <= to &&
addDaysIso(row.targetWeek, 6) >= from
);
) {
return true;
}
return filters.dateRange === null && isOpenUndated(row);
};
const matchesSite: AdvancedFilterPredicate = (row, filters) =>

View file

@ -488,7 +488,21 @@ describe("matchesAdvancedFilters", () => {
expect(matchesAdvancedFilters(weekOnlyOutside, filters)).toBe(false);
expect(matchesAdvancedFilters(datedOutsideWithWeek, filters)).toBe(false);
expect(matchesAdvancedFilters(undated, filters)).toBe(false);
expect(matchesAdvancedFilters(undated, { ...filters, dateRange: null })).toBe(true);
// No range: every week, plus open undated rows. Closed undated rows are
// never unscheduled, with or without the Unscheduled filter.
const noRange = { ...filters, dateRange: null };
const unscheduledOnly = { ...filters, unscheduledOnly: true };
const completedUndated = makeRow({ scheduledOn: "", targetWeek: "", status: "Completed" });
const canceledUndated = makeRow({ scheduledOn: "", targetWeek: "", status: "Canceled" });
expect(matchesAdvancedFilters(undated, noRange)).toBe(true);
expect(matchesAdvancedFilters(datedOutsideWithWeek, noRange)).toBe(true);
expect(matchesAdvancedFilters(weekOnlyOutside, noRange)).toBe(true);
expect(matchesAdvancedFilters(completedUndated, noRange)).toBe(false);
expect(matchesAdvancedFilters(canceledUndated, noRange)).toBe(false);
expect(matchesAdvancedFilters(undated, unscheduledOnly)).toBe(true);
expect(matchesAdvancedFilters(completedUndated, unscheduledOnly)).toBe(false);
expect(matchesAdvancedFilters(canceledUndated, unscheduledOnly)).toBe(false);
});
});