;
diff --git a/src/domain/work-orders/types/work-order-board.ts b/src/domain/work-orders/types/work-order-board.ts
index cbd48acd..b8bdcaa0 100644
--- a/src/domain/work-orders/types/work-order-board.ts
+++ b/src/domain/work-orders/types/work-order-board.ts
@@ -1,6 +1,11 @@
import type { DateRangeKey } from "./work-order-advanced-filters";
import type { WorkOrderAdditionalContact } from "./work-order-additional-contact";
-import type { WorkOrderTableRow } from "./work-order-table-row";
+import type {
+ WorkOrderFrozenCompany,
+ WorkOrderFrozenPoc,
+ WorkOrderFrozenSite,
+ WorkOrderTableRow,
+} from "./work-order-table-row";
import type { WizardWOStatus, WOType } from "./work-order-wizard";
/** Numeric WorkOrderType enum as expected by POST /workorders/board (no string enums). */
@@ -173,6 +178,9 @@ export interface WorkOrderBoardRowApi {
pocPhone?: string | null;
pocNotes?: string | null;
additionalContacts?: WorkOrderAdditionalContact[] | null;
+ frozenSite?: WorkOrderFrozenSite | null;
+ frozenCompany?: WorkOrderFrozenCompany | null;
+ frozenPoc?: WorkOrderFrozenPoc | null;
lifecycleStatus?: number | null;
lifecycleStatusLabel?: string | null;
legacyStatus?: string | null;
diff --git a/src/domain/work-orders/types/work-order-table-row.ts b/src/domain/work-orders/types/work-order-table-row.ts
index 92040fa2..d362f931 100644
--- a/src/domain/work-orders/types/work-order-table-row.ts
+++ b/src/domain/work-orders/types/work-order-table-row.ts
@@ -13,6 +13,35 @@ export type WorkOrderUpliftSummary = {
primaryStatus?: string;
};
+export type WorkOrderFrozenSite = {
+ code?: string;
+ label?: string;
+ address?: string;
+ city?: string;
+ state?: string;
+ zip?: string;
+ phone?: string;
+ email?: string;
+ maps?: string;
+};
+
+export type WorkOrderFrozenCompany = {
+ name?: string;
+ contactName?: string;
+ address?: string;
+ city?: string;
+ state?: string;
+ zip?: string;
+ phone?: string;
+ email?: string;
+ maps?: string;
+};
+
+export type WorkOrderFrozenPoc = {
+ contacts: { name?: string; phone?: string; notes?: string }[];
+ notes?: string;
+};
+
export interface WorkOrderTableRow {
id: string | number;
woNumber: string;
@@ -49,6 +78,9 @@ export interface WorkOrderTableRow {
pocPhone: string;
pocNotes: string;
additionalContacts: WorkOrderAdditionalContact[];
+ frozenSite?: WorkOrderFrozenSite;
+ frozenCompany?: WorkOrderFrozenCompany;
+ frozenPoc?: WorkOrderFrozenPoc;
dueDate: string;
rescheduleCount: number;
carriedOver: number;
diff --git a/src/domain/work-orders/utils/completion-doc-html.ts b/src/domain/work-orders/utils/completion-doc-html.ts
index 87d3b30c..a1114090 100644
--- a/src/domain/work-orders/utils/completion-doc-html.ts
+++ b/src/domain/work-orders/utils/completion-doc-html.ts
@@ -75,11 +75,11 @@ export function buildDocHtml(
${esc(serviceTitle(row).toUpperCase())}
WO# ${esc(row.woNumber)}
- Site ID — ${esc(row.site)}
- Amazon.com Services LLC ${esc(row.site)}
+ Site ID — ${esc(fields.siteCode)}
+ Amazon.com Services LLC ${esc(fields.siteCode)}
${esc(fields.street)},
${esc(cityStateZip)}
- Vendor / Technician: ${esc(row.tech || "—")}${row.company ? ` · ${esc(row.company)}` : ""}
+ Vendor / Technician: ${esc(fields.tech || "—")}${fields.company ? ` · ${esc(fields.company)}` : ""}
${completedBlock}
${esc(proc.heading)}:
diff --git a/src/domain/work-orders/utils/completion-doc-site.ts b/src/domain/work-orders/utils/completion-doc-site.ts
index b1b9dc18..1546fdcc 100644
--- a/src/domain/work-orders/utils/completion-doc-site.ts
+++ b/src/domain/work-orders/utils/completion-doc-site.ts
@@ -1,31 +1,87 @@
import type { Location } from "@/domain/locations/types/location";
import type { CompDocSiteInfo } from "@/domain/work-orders/types/completion-doc";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
+import {
+ effectiveCompany,
+ effectiveSite,
+} from "@/domain/work-orders/utils/effective-completion-snapshot";
+
+type CompDocAddress = Pick<
+ CompDocSiteInfo,
+ "street" | "city" | "state" | "zip" | "label" | "fullAddress"
+>;
+
+type CompDocIdentityRow = Pick &
+ Partial>;
function buildLabel(city?: string, state?: string): string {
return [city, state].filter(Boolean).join(", ");
}
-export function resolveCompDocSite(
- row: Pick,
- location?: Location | null,
-): CompDocSiteInfo {
- if (location?.address || location?.city) {
- const city = location.city ?? "";
- const state = location.state ?? "";
- const zip = location.zipCode ?? "";
- const label = buildLabel(city, state);
- const street = location.address ?? "";
- const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
- return { street, city, state, zip, label, fullAddress };
- }
+function toCompDocAddress(
+ street: string,
+ city: string,
+ state: string,
+ zip: string,
+): CompDocAddress {
+ const label = buildLabel(city, state);
+ const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
+ return { street, city, state, zip, label, fullAddress };
+}
- const locationParts = row.location.split(",").map((s) => s.trim());
+function withCompDocIdentity(address: CompDocAddress, row: CompDocIdentityRow): CompDocSiteInfo {
+ const frozen = effectiveSite(row);
+ const company = effectiveCompany({
+ status: row.status,
+ company: row.company ?? "",
+ tech: row.tech ?? "",
+ techPhone: row.techPhone ?? "",
+ frozenCompany: row.frozenCompany,
+ });
+ return {
+ ...address,
+ code: frozen?.code ?? row.site,
+ tech: company.contactName ?? "",
+ company: company.name ?? "",
+ };
+}
+
+function resolveLiveLocationSite(location: Location): CompDocAddress {
+ return toCompDocAddress(
+ location.address ?? "",
+ location.city ?? "",
+ location.state ?? "",
+ location.zipCode ?? "",
+ );
+}
+
+function resolveRowLocationSite(row: Pick): CompDocAddress {
+ const locationParts = row.location.split(",").map((part) => part.trim());
const city = locationParts[0] ?? "";
const state = locationParts[1] ?? "";
const label = buildLabel(city, state);
const street = row.location && !label ? row.location : "";
- const fullAddress = [street, label].filter(Boolean).join(", ") || "—";
-
- return { street, city, state, zip: "", label, fullAddress };
+ return toCompDocAddress(street, city, state, "");
+}
+
+export function resolveCompDocSite(
+ row: CompDocIdentityRow,
+ location?: Location | null,
+): CompDocSiteInfo {
+ const frozen = effectiveSite(row);
+ if (frozen != null) {
+ return withCompDocIdentity(
+ toCompDocAddress(
+ frozen.address ?? "",
+ frozen.city ?? "",
+ frozen.state ?? "",
+ frozen.zip ?? "",
+ ),
+ row,
+ );
+ }
+ if (location?.address || location?.city) {
+ return withCompDocIdentity(resolveLiveLocationSite(location), row);
+ }
+ return withCompDocIdentity(resolveRowLocationSite(row), row);
}
diff --git a/src/domain/work-orders/utils/effective-completion-snapshot.ts b/src/domain/work-orders/utils/effective-completion-snapshot.ts
new file mode 100644
index 00000000..b552b39c
--- /dev/null
+++ b/src/domain/work-orders/utils/effective-completion-snapshot.ts
@@ -0,0 +1,68 @@
+import type { WorkOrderAdditionalContact } from "@/domain/work-orders/types/work-order-additional-contact";
+import type {
+ WorkOrderFrozenCompany,
+ WorkOrderFrozenPoc,
+ WorkOrderFrozenSite,
+ WorkOrderTableRow,
+} from "@/domain/work-orders/types/work-order-table-row";
+
+type SiteSnapshotRow = Pick;
+type CompanySnapshotRow = Pick<
+ WorkOrderTableRow,
+ "status" | "company" | "tech" | "techPhone" | "frozenCompany"
+>;
+type PocSnapshotRow = Pick<
+ WorkOrderTableRow,
+ "status" | "pocName" | "pocPhone" | "pocNotes" | "frozenPoc"
+> & {
+ additionalContacts?: WorkOrderTableRow["additionalContacts"];
+};
+
+export function effectiveSite(row: SiteSnapshotRow): WorkOrderFrozenSite | undefined {
+ if (row.status === "Completed" && row.frozenSite != null) return row.frozenSite;
+ return undefined;
+}
+
+export function effectiveSitePlace(row: SiteSnapshotRow): {
+ city: string;
+ state: string;
+} | null {
+ const frozen = effectiveSite(row);
+ if (frozen == null) return null;
+ return { city: frozen.city ?? "", state: frozen.state ?? "" };
+}
+
+export function effectiveCompany(row: CompanySnapshotRow): WorkOrderFrozenCompany {
+ if (row.status === "Completed" && row.frozenCompany != null) return row.frozenCompany;
+ return {
+ name: row.company,
+ contactName: row.tech,
+ phone: row.techPhone,
+ };
+}
+
+export function effectivePoc(row: PocSnapshotRow): WorkOrderFrozenPoc {
+ if (row.status === "Completed" && row.frozenPoc != null) return row.frozenPoc;
+ return {
+ contacts: [
+ { name: row.pocName, phone: row.pocPhone },
+ ...(row.additionalContacts ?? []).map((contact) => ({
+ name: contact.name,
+ phone: contact.phone,
+ notes: contact.notes,
+ })),
+ ],
+ notes: row.pocNotes,
+ };
+}
+
+export function effectiveAdditionalContacts(row: PocSnapshotRow): WorkOrderAdditionalContact[] {
+ if (row.status === "Completed" && row.frozenPoc != null) {
+ return row.frozenPoc.contacts.slice(1).map((contact) => ({
+ name: contact.name ?? "",
+ phone: contact.phone ?? "",
+ notes: contact.notes ?? "",
+ }));
+ }
+ return row.additionalContacts ?? [];
+}
diff --git a/src/test/app/(protected)/workorders/use-comp-doc-dialog-address-seed.test.tsx b/src/test/app/(protected)/workorders/use-comp-doc-dialog-address-seed.test.tsx
index 80a9b011..3635974c 100644
--- a/src/test/app/(protected)/workorders/use-comp-doc-dialog-address-seed.test.tsx
+++ b/src/test/app/(protected)/workorders/use-comp-doc-dialog-address-seed.test.tsx
@@ -9,11 +9,16 @@ import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-ta
const locationDetailState = vi.hoisted(() => ({
data: undefined as Location | undefined,
}));
+const updateLocation = vi.hoisted(() => vi.fn());
vi.mock("@/domain/locations/use-cases/use-location-detail", () => ({
useLocationDetail: () => ({ data: locationDetailState.data }),
}));
+vi.mock("@/domain/locations/api/locations-api", () => ({
+ locationsApi: { update: updateLocation },
+}));
+
function baseRow(overrides: Partial = {}): WorkOrderTableRow {
const defaults: WorkOrderTableRow = {
id: 42,
@@ -80,6 +85,7 @@ function wrapper({ children }: { children: ReactNode }) {
describe("useCompDocDialog address seeding", () => {
beforeEach(() => {
locationDetailState.data = undefined;
+ updateLocation.mockReset();
});
it("seeds address from the first authoritative locationDetail without locking on the empty fallback", async () => {
@@ -153,4 +159,39 @@ describe("useCompDocDialog address seeding", () => {
expect(result.current.editAddress).toBe("Dispatcher typed address");
});
+
+ it("does not update the live location when a completed work order uses a frozen site", async () => {
+ const onSave = vi.fn().mockResolvedValue(undefined);
+ const row = baseRow({
+ status: "Completed",
+ frozenSite: {
+ address: "100 Frozen Ave",
+ city: "Chicago",
+ state: "IL",
+ zip: "60601",
+ },
+ });
+ const { result } = renderHook(
+ () =>
+ useCompDocDialog({
+ row,
+ open: true,
+ onSave,
+ }),
+ { wrapper },
+ );
+
+ await waitFor(() => {
+ expect(result.current.editAddress).toBe("100 Frozen Ave");
+ });
+ act(() => {
+ result.current.setEditAddress("Edited document address");
+ });
+ await act(async () => {
+ await result.current.handleSaveDraft();
+ });
+
+ expect(updateLocation).not.toHaveBeenCalled();
+ expect(onSave).toHaveBeenCalledWith(expect.any(Object), "No");
+ });
});
diff --git a/src/test/app/(protected)/workorders/use-site-dialog-state.test.tsx b/src/test/app/(protected)/workorders/use-site-dialog-state.test.tsx
index b3f72c52..d527414d 100644
--- a/src/test/app/(protected)/workorders/use-site-dialog-state.test.tsx
+++ b/src/test/app/(protected)/workorders/use-site-dialog-state.test.tsx
@@ -5,19 +5,31 @@ import type { Location } from "@/domain/locations/types/location";
import type { LocationOption } from "@/domain/work-orders/types/work-order";
const detailById = new Map();
+const requestedDetailIds: Array = [];
+const EMPTY_CONTACTS: [] = [];
vi.mock("@/domain/locations/use-cases/use-location-detail", () => ({
- useLocationDetail: (id: string | undefined) => ({
- data: id && detailById.has(id) ? detailById.get(id) : undefined,
- isLoading: Boolean(id && !detailById.has(id)),
- isError: false,
- }),
+ useLocationDetail: (id: string | undefined) => {
+ requestedDetailIds.push(id);
+ return {
+ data: id && detailById.has(id) ? detailById.get(id) : undefined,
+ isLoading: Boolean(id && !detailById.has(id)),
+ isError: false,
+ };
+ },
}));
const SITES: LocationOption[] = [
{ id: "loc-1", name: "BK5 · Dallas", siteCode: "BK5", city: "Dallas", state: "TX" },
{ id: "loc-2", name: "SEA1 · Seattle", siteCode: "SEA1", city: "Seattle", state: "WA" },
];
+const FROZEN_SITE = {
+ label: "BK5 · Frozen",
+ address: "100 Frozen Ave",
+ city: "Chicago",
+ state: "IL",
+ zip: "60601",
+};
function locationDetail(id: string, contact: string, phone: string): Location {
return {
@@ -42,7 +54,7 @@ function renderSiteDialogState() {
pocName: "",
pocPhone: "",
pocNotes: "",
- additionalContacts: [],
+ additionalContacts: EMPTY_CONTACTS,
sites: SITES,
createMode: true,
onSave: vi.fn(),
@@ -53,6 +65,7 @@ function renderSiteDialogState() {
describe("useSiteDialogState POC autofill", () => {
beforeEach(() => {
detailById.clear();
+ requestedDetailIds.length = 0;
});
it("preserves POC typed before deferred location detail resolves", () => {
@@ -110,4 +123,30 @@ describe("useSiteDialogState POC autofill", () => {
expect(result.current.pn).toBe("Contact B");
expect(result.current.pp).toBe("222-222-2222");
});
+
+ it("does not request live location detail when a frozen site is provided", () => {
+ const { result } = renderHook(() =>
+ useSiteDialogState({
+ open: true,
+ onOpenChange: vi.fn(),
+ value: "BK5",
+ locationId: "loc-1",
+ pocName: "Frozen POC",
+ pocPhone: "111",
+ pocNotes: "",
+ additionalContacts: EMPTY_CONTACTS,
+ sites: SITES,
+ createMode: false,
+ frozenSite: FROZEN_SITE,
+ onSave: vi.fn(),
+ }),
+ );
+
+ expect(requestedDetailIds).not.toContain("loc-1");
+ expect(result.current.locationDetail).toMatchObject({
+ address: "100 Frozen Ave",
+ city: "Chicago",
+ state: "IL",
+ });
+ });
});
diff --git a/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx b/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx
new file mode 100644
index 00000000..413eb2f1
--- /dev/null
+++ b/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx
@@ -0,0 +1,90 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+import { VendorDialogBody } from "@/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-body";
+import { VendorDialogCompanyCard } from "@/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card";
+import type { VendorDropdownItem } from "@/domain/work-orders/types/work-order";
+
+const LIVE_VENDOR: VendorDropdownItem = {
+ id: 10,
+ companyName: "Live Vendor",
+ contactName: "Live Contact",
+ tradeSpecialties: "Live Trade",
+ address: "900 Live St",
+ distanceMiles: 12,
+ email: "live@example.com",
+};
+
+describe("VendorDialogCompanyCard completion snapshot", () => {
+ it("does not fill missing frozen fields from the live vendor registry", () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText("Frozen Vendor")).toBeInTheDocument();
+ expect(screen.queryByText("Live Contact")).not.toBeInTheDocument();
+ expect(screen.queryByText("Live Trade")).not.toBeInTheDocument();
+ expect(screen.queryByText("live@example.com")).not.toBeInTheDocument();
+ expect(screen.queryByRole("link", { name: /open in google maps/i })).not.toBeInTheDocument();
+ });
+
+ it("renders the complete frozen address instead of the live vendor address", () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText("200 Vendor Ave, Chicago, IL, 60602")).toBeInTheDocument();
+ expect(screen.queryByText("900 Live St")).not.toBeInTheDocument();
+ });
+
+ it("shows the frozen technician instead of a renamed live roster contact", () => {
+ render(
+ undefined}
+ onPick={() => undefined}
+ onPhoneChange={() => undefined}
+ onNotesChange={() => undefined}
+ onTechnicianCreated={() => undefined}
+ />,
+ );
+
+ expect(screen.getAllByText("Frozen Tech").length).toBeGreaterThan(0);
+ expect(screen.queryByText("Live Contact")).not.toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: /\+ add technician/i })).not.toBeInTheDocument();
+ });
+});
diff --git a/src/test/domain/work-orders/mappers/work-order-table-row-mapper.test.ts b/src/test/domain/work-orders/mappers/work-order-table-row-mapper.test.ts
index 0b0e78c5..9315fd9d 100644
--- a/src/test/domain/work-orders/mappers/work-order-table-row-mapper.test.ts
+++ b/src/test/domain/work-orders/mappers/work-order-table-row-mapper.test.ts
@@ -186,3 +186,45 @@ describe("mapWorkOrderTableRow carry-over fields", () => {
expect(group.items.map((item) => item.id)).toEqual([42]);
});
});
+
+describe("mapWorkOrderTableRow completion snapshot", () => {
+ it("maps frozen site, company, and POC payloads", () => {
+ const row = mapWorkOrderTableRow({
+ id: 191,
+ lifecycleStatus: 7,
+ frozenSite: { code: "BK5", label: "BK5 Chicago", city: "Chicago", state: "IL" },
+ frozenCompany: { name: "Frozen Co", contactName: "Frozen Tech", phone: "111" },
+ frozenPoc: {
+ contacts: [{ name: "Frozen POC", phone: "222" }],
+ notes: "Call first",
+ },
+ });
+
+ expect(row.frozenSite).toMatchObject({ code: "BK5", city: "Chicago", state: "IL" });
+ expect(row.frozenCompany).toMatchObject({
+ name: "Frozen Co",
+ contactName: "Frozen Tech",
+ phone: "111",
+ });
+ expect(row.frozenPoc).toEqual({
+ contacts: [{ name: "Frozen POC", phone: "222", notes: "" }],
+ notes: "Call first",
+ });
+ });
+
+ it("maps frozen POC contact notes when the payload supplies them", () => {
+ const row = mapWorkOrderTableRow({
+ id: 192,
+ lifecycleStatus: 7,
+ frozenPoc: {
+ contacts: [{ name: "Frozen POC", phone: "222", notes: "Gate code 9" }],
+ notes: "Call first",
+ },
+ });
+
+ expect(row.frozenPoc).toEqual({
+ contacts: [{ name: "Frozen POC", phone: "222", notes: "Gate code 9" }],
+ notes: "Call first",
+ });
+ });
+});
diff --git a/src/test/domain/work-orders/utils/completion-doc-site.test.ts b/src/test/domain/work-orders/utils/completion-doc-site.test.ts
new file mode 100644
index 00000000..4a2cb2f9
--- /dev/null
+++ b/src/test/domain/work-orders/utils/completion-doc-site.test.ts
@@ -0,0 +1,62 @@
+import { describe, expect, it } from "vitest";
+import { resolveCompDocSite } from "@/domain/work-orders/utils/completion-doc-site";
+
+describe("resolveCompDocSite", () => {
+ it("prefers a completed work order snapshot over conflicting live location data", () => {
+ const result = resolveCompDocSite(
+ {
+ site: "BK5",
+ location: "Live City, WI",
+ status: "Completed",
+ company: "Live Co",
+ tech: "Live Tech",
+ frozenSite: {
+ code: "FROZEN",
+ address: "100 Frozen Ave",
+ city: "Chicago",
+ state: "IL",
+ zip: "60601",
+ },
+ frozenCompany: { name: "Frozen Co", contactName: "Frozen Tech" },
+ },
+ {
+ address: "900 Live St",
+ city: "Milwaukee",
+ state: "WI",
+ zipCode: "53202",
+ },
+ );
+
+ expect(result).toEqual({
+ code: "FROZEN",
+ street: "100 Frozen Ave",
+ city: "Chicago",
+ state: "IL",
+ zip: "60601",
+ label: "Chicago, IL",
+ fullAddress: "100 Frozen Ave, Chicago, IL, 60601",
+ tech: "Frozen Tech",
+ company: "Frozen Co",
+ });
+ });
+
+ it("keeps using the live location for an open work order", () => {
+ const result = resolveCompDocSite(
+ {
+ site: "BK5",
+ location: "Old City, IL",
+ status: "Scheduled",
+ frozenSite: { address: "100 Frozen Ave", city: "Chicago", state: "IL" },
+ },
+ {
+ address: "900 Live St",
+ city: "Milwaukee",
+ state: "WI",
+ zipCode: "53202",
+ },
+ );
+
+ expect(result.street).toBe("900 Live St");
+ expect(result.label).toBe("Milwaukee, WI");
+ });
+});
diff --git a/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts b/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts
new file mode 100644
index 00000000..5044a09a
--- /dev/null
+++ b/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts
@@ -0,0 +1,116 @@
+import { describe, expect, it } from "vitest";
+import {
+ effectiveAdditionalContacts,
+ effectiveCompany,
+ effectivePoc,
+ effectiveSite,
+ effectiveSitePlace,
+} from "@/domain/work-orders/utils/effective-completion-snapshot";
+
+describe("effective completion snapshot", () => {
+ it("prefers frozen values for a completed work order", () => {
+ const frozenSite = { code: "BK5", city: "Chicago", state: "IL" };
+ expect(effectiveSite({ status: "Completed", frozenSite })).toEqual(frozenSite);
+ expect(effectiveSitePlace({ status: "Completed", frozenSite })).toEqual({
+ city: "Chicago",
+ state: "IL",
+ });
+
+ expect(
+ effectiveCompany({
+ status: "Completed",
+ company: "Live Co",
+ tech: "Live Tech",
+ techPhone: "222",
+ frozenCompany: { name: "Frozen Co", contactName: "Frozen Tech", phone: "111" },
+ }),
+ ).toMatchObject({ name: "Frozen Co", contactName: "Frozen Tech", phone: "111" });
+
+ expect(
+ effectivePoc({
+ status: "Completed",
+ pocName: "Live POC",
+ pocPhone: "222",
+ pocNotes: "live",
+ additionalContacts: [],
+ frozenPoc: {
+ contacts: [{ name: "Frozen POC", phone: "111" }],
+ notes: "frozen",
+ },
+ }),
+ ).toEqual({
+ contacts: [{ name: "Frozen POC", phone: "111" }],
+ notes: "frozen",
+ });
+ });
+
+ it("uses live values while open or when a completed snapshot is missing", () => {
+ expect(effectiveSite({ status: "Scheduled", frozenSite: { city: "Old" } })).toBeUndefined();
+ expect(effectiveSitePlace({ status: "Completed" })).toBeNull();
+ expect(
+ effectiveCompany({
+ status: "Scheduled",
+ company: "Live Co",
+ tech: "Live Tech",
+ techPhone: "222",
+ frozenCompany: { name: "Old Co" },
+ }),
+ ).toMatchObject({ name: "Live Co", contactName: "Live Tech", phone: "222" });
+ expect(
+ effectivePoc({
+ status: "Completed",
+ pocName: "Live POC",
+ pocPhone: "222",
+ pocNotes: "live",
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
+ }),
+ ).toEqual({
+ contacts: [
+ { name: "Live POC", phone: "222" },
+ { name: "Backup", phone: "333", notes: "gate hours" },
+ ],
+ notes: "live",
+ });
+ expect(
+ effectiveAdditionalContacts({
+ status: "Scheduled",
+ pocName: "Live POC",
+ pocPhone: "222",
+ pocNotes: "live",
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
+ }),
+ ).toEqual([{ name: "Backup", phone: "333", notes: "gate hours" }]);
+ });
+
+ it("keeps extra-contact notes on the open work-order path", () => {
+ expect(
+ effectiveAdditionalContacts({
+ status: "Scheduled",
+ pocName: "Primary",
+ pocPhone: "111",
+ pocNotes: "",
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
+ frozenPoc: {
+ contacts: [
+ { name: "Frozen", phone: "000" },
+ { name: "Frozen Extra", phone: "999", notes: "ignored while open" },
+ ],
+ },
+ }),
+ ).toEqual([{ name: "Backup", phone: "333", notes: "gate hours" }]);
+ });
+
+ it("treats missing additional contacts as an empty live contact list", () => {
+ expect(
+ effectivePoc({
+ status: "Scheduled",
+ pocName: "Primary",
+ pocPhone: "111",
+ pocNotes: "",
+ }),
+ ).toEqual({
+ contacts: [{ name: "Primary", phone: "111" }],
+ notes: "",
+ });
+ });
+});