import { mapApiStatusToWizard } from "@/domain/work-orders/mappers/wizard-draft-mapper"; import type { DispatchChecklistItem, DispatchComment, DispatchDetail, DispatchSignoff, DispatchToVendorPayload, DispatchWorkOrderRef, EmployeeOption, LocationOption, TaskListTemplate, UpdateDispatchPayload, WorkOrderAuditEntry, WorkOrderComment, WorkOrderDetail, WorkOrderDispatchSummary, WorkOrderFormValues, WorkOrderListItem, } from "@/domain/work-orders/types/work-order"; import type { CompletionDocStatus, WorkOrderTableRow, } from "@/domain/work-orders/types/work-order-table-row"; import type { WOType } from "@/domain/work-orders/types/work-order-wizard"; import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils"; import { formatApptRange } from "@/domain/work-orders/utils/wizard-date-utils"; import type { UpliftRequest } from "@/domain/uplifts/types/uplift"; import type { WorkOrderStatus } from "@/domain/work-orders/types/work-order-common"; import { WORK_ORDER_STATUS_OPTIONS } from "@/domain/work-orders/types/work-order-common"; import { toDateInputValue } from "@/lib/time-utils"; function asRecord(raw: unknown): Record { return typeof raw === "object" && raw !== null ? (raw as Record) : {}; } function toWorkOrderStatus(value: string): WorkOrderStatus { if (WORK_ORDER_STATUS_OPTIONS.includes(value as WorkOrderStatus)) { return value as WorkOrderStatus; } return "Open"; } function readString(record: Record, ...keys: string[]): string { for (const key of keys) { const value = record[key]; if (typeof value === "string") { return value; } if (typeof value === "number") { return String(value); } } return ""; } function readId(record: Record): string | number { const value = record.id ?? record.Id; if (typeof value === "string" || typeof value === "number") { return value; } return ""; } function readNumber(record: Record, ...keys: string[]): number | null { for (const key of keys) { const value = record[key]; if (typeof value === "number") { return value; } if (typeof value === "string" && value !== "") { const parsed = Number(value); if (!Number.isNaN(parsed)) { return parsed; } } } return null; } function readBool(record: Record, ...keys: string[]): boolean { for (const key of keys) { const value = record[key]; if (typeof value === "boolean") { return value; } } return false; } export function mapWorkOrderListItem(raw: unknown): WorkOrderListItem { const item = asRecord(raw); return { id: readId(item), number: readString(item, "internalWONumber", "number", "workOrderNumber"), title: readString(item, "title", "Title"), location: readString(item, "location", "Location", "locationName", "LocationName"), priority: readString(item, "priority", "Priority"), status: readString(item, "status", "Status"), createdDate: readString(item, "createdDate", "CreatedDate"), dueDate: readString(item, "dueDate", "DueDate"), assignedTo: readString(item, "assignedTo", "AssignedTo", "employeeName", "EmployeeName"), lastUpdated: readString(item, "lastUpdated", "LastUpdated"), }; } export function mapWorkOrderListItems(rawItems: unknown[]): WorkOrderListItem[] { return rawItems.map(mapWorkOrderListItem); } const WO_TYPES_SET = new Set(["PM", "Reactive", "Emergency", "Add-On", "Overdue"]); function readWoType(record: Record): WOType | "" { const raw = readString(record, "type", "Type", "source", "Source", "woType", "WoType"); if (WO_TYPES_SET.has(raw)) { return raw as WOType; } return ""; } function readDocStatus(record: Record): CompletionDocStatus { const raw = readString(record, "docStatus", "DocStatus", "completionDoc", "CompletionDoc"); if (raw === "Yes" || raw === "No" || raw === "NN") { return raw; } return ""; } export function mapWorkOrderTableRow(raw: unknown): WorkOrderTableRow { const item = asRecord(raw); const scheduledOn = toDateInputValue( readString(item, "scheduledOn", "scheduledDate", "ScheduledDate", "ScheduledOn"), ); const { dayGroup, dayLabel } = deriveDayFields(scheduledOn); const woNumber = readString( item, "woNumber", "internalWONumber", "InternalWONumber", "number", "workOrderNumber", "WorkerOrderNumber", ); const site = readString(item, "site", "Site", "locationCode", "LocationCode"); const location = readString(item, "location", "Location", "locationName", "LocationName"); const dispatcherId = readString( item, "dispatcherId", "DispatcherId", "assigneeId", "AssigneeId", "employeeId", "EmployeeId", ); const dispatcherName = readString( item, "dispatcherName", "DispatcherName", "assignedTo", "AssignedTo", "employeeName", "EmployeeName", "assignee", "Assignee", ); const apptStart = readString(item, "apptStart", "ApptStart", "appointmentStart"); const apptEnd = readString(item, "apptEnd", "ApptEnd", "appointmentEnd"); const apptTime = readString(item, "apptTime", "ApptTime") || (apptStart ? formatApptRange(apptStart, apptEnd) : ""); const apiStatus = readString(item, "status", "Status"); const locationIdRaw = item.locationId ?? item.LocationId; const row: WorkOrderTableRow = { id: readId(item), woNumber, site: site || location.slice(0, 8), type: readWoType(item), dispatcherId, dispatcherName, scheduledOn, completedDate: toDateInputValue(readString(item, "completedDate", "CompletedDate")), targetWeek: readString(item, "targetWeek", "TargetWeek"), dayGroup, dayLabel, location, locationId: typeof locationIdRaw === "string" || typeof locationIdRaw === "number" ? locationIdRaw : "", pm: readString(item, "pm", "PM", "problem", "Problem", "service", "Service"), company: readString(item, "company", "Company", "vendorName", "VendorName"), tech: readString(item, "tech", "Tech", "technician", "Technician"), techPhone: readString(item, "techPhone", "TechPhone"), apptStart, apptEnd, apptTime, status: mapApiStatusToWizard(apiStatus), docStatus: readDocStatus(item), pocName: readString(item, "pocName", "PocName", "POCName"), pocPhone: readString(item, "pocPhone", "PocPhone", "POCPhone"), pocNotes: readString(item, "pocNotes", "PocNotes", "POCNotes"), dueDate: toDateInputValue(readString(item, "dueDate", "DueDate")), rescheduleCount: readNumber(item, "rescheduleCount", "RescheduleCount") ?? 0, carriedOver: readNumber(item, "carriedOver", "CarriedOver") ?? 0, originalWeek: readString(item, "originalWeek", "OriginalWeek"), originalDate: toDateInputValue(readString(item, "originalDate", "OriginalDate")), isPastDue: readBool(item, "isPastDue", "IsPastDue"), scheduleWeekOnly: readBool(item, "scheduleWeekOnly", "ScheduleWeekOnly"), mediaCount: readNumber(item, "mediaCount", "MediaCount") ?? 0, title: readString(item, "title", "Title", "workerOrderTitle", "WorkerOrderTitle"), priority: readString(item, "priority", "Priority"), }; return row; } export function mapWorkOrderTableRows(rawItems: unknown[]): WorkOrderTableRow[] { return rawItems.map(mapWorkOrderTableRow); } export function mapWorkOrderComment(raw: unknown): WorkOrderComment { const item = asRecord(raw); return { id: readId(item), createdDate: readString(item, "createdDate", "CreatedDate"), commenttext: readString(item, "commenttext", "Commenttext", "text", "Text"), commentType: readString(item, "commentType", "CommentType"), userName: readString(item, "userName", "UserName"), commenter: readString(item, "commenter", "Commenter") || null, recordType: readString(item, "recordType", "RecordType") || null, }; } export function mapWorkOrderAuditEntry(raw: unknown): WorkOrderAuditEntry { const item = asRecord(raw); return { id: readId(item), fieldName: readString(item, "fieldName", "FieldName"), oldValue: readString(item, "oldValue", "OldValue"), newValue: readString(item, "newValue", "NewValue"), action: readString(item, "action", "Action"), createdAt: readString(item, "createdAt", "CreatedAt"), userName: readString(item, "userName", "UserName"), }; } export function mapWorkOrderDispatchSummary(raw: unknown): WorkOrderDispatchSummary { const item = asRecord(raw); return { id: readId(item), vendorId: readId({ id: item.vendorId ?? item.VendorId }), dispatchNumber: readString(item, "dispatchNumber", "DispatchNumber"), poNumber: readString(item, "poNumber", "PoNumber", "PONumber"), vendorName: readString(item, "vendorName", "VendorName"), vendorEmail: readString(item, "vendorEmail", "VendorEmail"), nteAmount: readNumber(item, "nteAmount", "NTEAmount"), status: readString(item, "status", "Status"), dispatchedAt: readString(item, "dispatchedAt", "DispatchedAt"), estimatedArrivalAt: readString(item, "estimatedArrivalAt", "EstimatedArrivalAt"), etaManualOverride: readBool(item, "etaManualOverride", "EtaManualOverride"), }; } export function mapWorkOrderDetail(raw: unknown): WorkOrderDetail { const wo = asRecord(raw); const locationId = wo.locationId ?? wo.LocationId; return { id: readId(wo), internalWONumber: readString(wo, "internalWONumber", "InternalWONumber"), workOrderNumber: readString(wo, "workerOrderNumber", "workOrderNumber", "WorkerOrderNumber"), title: readString(wo, "title", "workerOrderTitle", "WorkerOrderTitle"), location: readString(wo, "location", "Location"), locationId: locationId != null ? String(locationId) : "", locationZip: readString(wo, "locationZip", "LocationZip"), assignedTo: readString(wo, "assignee", "assignedTo", "Assignee", "AssignedTo"), dueDate: toDateInputValue(readString(wo, "dueDate", "DueDate")), createdDate: toDateInputValue(readString(wo, "createdDate", "CreatedDate")), createdDateFull: readString(wo, "createdDate", "CreatedDate"), description: readString(wo, "description", "Description"), priority: readString(wo, "priority", "Priority"), status: readString(wo, "status", "Status") || "Open", po: readString(wo, "po", "PO"), tt: readString(wo, "tt", "TT"), problem: readString(wo, "problem", "Problem"), trade: readString(wo, "trade", "Trade"), subTrade: readString(wo, "subTrade", "SubTrade"), vendorNTE: wo.vendorNTE != null ? String(wo.vendorNTE) : "", scheduledDate: toDateInputValue(readString(wo, "scheduledDate", "ScheduledDate")), completedDate: toDateInputValue(readString(wo, "completedDate", "CompletedDate")), source: readString(wo, "source", "Source"), comments: Array.isArray(wo.comments) ? wo.comments.map(mapWorkOrderComment) : Array.isArray(wo.Comments) ? wo.Comments.map(mapWorkOrderComment) : [], auditLog: Array.isArray(wo.auditLog) ? wo.auditLog.map(mapWorkOrderAuditEntry) : Array.isArray(wo.AuditLog) ? wo.AuditLog.map(mapWorkOrderAuditEntry) : [], dispatches: Array.isArray(wo.dispatches) ? wo.dispatches.map(mapWorkOrderDispatchSummary) : Array.isArray(wo.Dispatches) ? wo.Dispatches.map(mapWorkOrderDispatchSummary) : [], }; } export function mapWorkOrderDetailToFormValues(detail: WorkOrderDetail): WorkOrderFormValues { return { internalWONumber: detail.internalWONumber, workOrderNumber: detail.workOrderNumber, title: detail.title, locationId: detail.locationId, priority: detail.priority, status: toWorkOrderStatus(detail.status), createdDate: detail.createdDate, dueDate: detail.dueDate, scheduledDate: detail.scheduledDate, completedDate: detail.completedDate, assignedTo: detail.assignedTo, description: detail.description, po: detail.po, tt: detail.tt, problem: detail.problem, trade: detail.trade, subTrade: detail.subTrade, vendorNTE: detail.vendorNTE, source: detail.source, }; } export function mapWorkOrderFormToCreatePayload( values: WorkOrderFormValues, ): Record { return { WorkerOrderNumber: values.workOrderNumber, WorkerOrderTitle: values.title, LocationId: values.locationId ? parseInt(values.locationId, 10) : null, Priority: values.priority || null, Status: values.status, CreatedDate: values.createdDate || null, DueDate: values.dueDate || null, ScheduledDate: values.scheduledDate || null, CompletedDate: values.completedDate || null, AssignTo: values.assignedTo || null, Description: values.description, PO: values.po || null, TT: values.tt || null, Problem: values.problem || null, Trade: values.trade || null, SubTrade: values.subTrade || null, VendorNTE: values.vendorNTE ? parseFloat(values.vendorNTE) : null, Source: values.source || null, }; } export function mapWorkOrderFormToUpdatePayload( id: string | number, values: WorkOrderFormValues, ): Record { return { Id: id, WorkerOrderNumber: values.workOrderNumber, WorkerOrderTitle: values.title, Description: values.description, PO: values.po || null, TT: values.tt || null, AssignTo: values.assignedTo || null, DueDate: values.dueDate || null, Priority: values.priority || null, Status: values.status, LocationId: values.locationId ? parseInt(values.locationId, 10) : null, Problem: values.problem || null, Trade: values.trade || null, SubTrade: values.subTrade || null, VendorNTE: values.vendorNTE ? parseFloat(values.vendorNTE) : null, ScheduledDate: values.scheduledDate || null, CompletedDate: values.completedDate || null, CreatedDate: values.createdDate || null, Source: values.source || null, }; } export function mapLocationOption(raw: unknown): LocationOption { const item = asRecord(raw); return { id: readId(item), name: readString(item, "name", "Name", "locationName", "LocationName"), }; } export function mapLocationOptions(rawItems: unknown[]): LocationOption[] { return rawItems.map(mapLocationOption); } export function mapEmployeeOption(raw: unknown): EmployeeOption { const item = asRecord(raw); return { id: readId(item), name: readString(item, "name", "Name", "fullName", "FullName", "employeeName", "EmployeeName") || readString(item, "userName", "UserName"), }; } export function mapEmployeeOptions(rawItems: unknown[]): EmployeeOption[] { return rawItems.map(mapEmployeeOption); } export { mapVendorDropdownItem, mapVendorDropdownItems } from "./work-order-vendor-mapper"; export function mapTaskListTemplate(raw: unknown): TaskListTemplate { const item = asRecord(raw); const items = item.items ?? item.Items; return { id: readId(item), name: readString(item, "name", "Name"), items: Array.isArray(items) ? items : [], }; } export function mapTaskListTemplates(rawItems: unknown[]): TaskListTemplate[] { return rawItems.map(mapTaskListTemplate); } export function mapDispatchChecklistItem(raw: unknown): DispatchChecklistItem { const item = asRecord(raw); return { id: readId(item), itemText: readString(item, "itemText", "ItemText"), isCompleted: readBool(item, "isCompleted", "IsCompleted"), completedBy: readString(item, "completedBy", "CompletedBy") || null, completedAt: readString(item, "completedAt", "CompletedAt") || null, }; } export function mapDispatchSignoff(raw: unknown): DispatchSignoff { const item = asRecord(raw); return { id: readId(item), signoffType: readString(item, "signoffType", "SignoffType"), name: readString(item, "name", "Name"), signature: readString(item, "signature", "Signature"), signatureMethod: readString(item, "signatureMethod", "SignatureMethod"), signedAt: readString(item, "signedAt", "SignedAt"), }; } export function mapDispatchComment(raw: unknown): DispatchComment { const item = asRecord(raw); return { id: readId(item), createdDate: readString(item, "createdDate", "CreatedDate"), commenttext: readString(item, "commenttext", "Commenttext", "text", "Text"), userName: readString(item, "userName", "UserName"), commenter: readString(item, "commenter", "Commenter") || null, }; } export function mapDispatchWorkOrderRef(raw: unknown): DispatchWorkOrderRef { const item = asRecord(raw); return { id: readId(item), woNumber: readString(item, "woNumber", "WoNumber", "WONumber", "number", "Number"), }; } export function mapDispatchDetail(raw: unknown): DispatchDetail { const item = asRecord(raw); const checklistItems = item.checklistItems ?? item.ChecklistItems; const signoffs = item.signoffs ?? item.Signoffs; const comments = item.comments ?? item.Comments; const workOrders = item.workOrders ?? item.WorkOrders; return { id: readId(item), dispatchNumber: readString(item, "dispatchNumber", "DispatchNumber"), poNumber: readString(item, "poNumber", "PoNumber", "PONumber"), vendorName: readString(item, "vendorName", "VendorName"), vendorEmail: readString(item, "vendorEmail", "VendorEmail"), woNumber: readString(item, "woNumber", "WoNumber"), status: readString(item, "status", "Status"), nteAmount: readNumber(item, "nteAmount", "NTEAmount"), scheduledDate: readString(item, "scheduledDate", "ScheduledDate"), estimatedArrivalAt: readString(item, "estimatedArrivalAt", "EstimatedArrivalAt"), etaManualOverride: readBool(item, "etaManualOverride", "EtaManualOverride"), completedDate: readString(item, "completedDate", "CompletedDate"), description: readString(item, "description", "Description"), dispatchedAt: readString(item, "dispatchedAt", "DispatchedAt"), acknowledgedAt: readString(item, "acknowledgedAt", "AcknowledgedAt"), verifiedAt: readString(item, "verifiedAt", "VerifiedAt"), verifiedBy: readString(item, "verifiedBy", "VerifiedBy"), checklistItems: Array.isArray(checklistItems) ? checklistItems.map(mapDispatchChecklistItem) : [], signoffs: Array.isArray(signoffs) ? signoffs.map(mapDispatchSignoff) : [], comments: Array.isArray(comments) ? comments.map(mapDispatchComment) : [], workOrders: Array.isArray(workOrders) ? workOrders.map(mapDispatchWorkOrderRef) : [], }; } export function mapDispatchToVendorPayload( payload: DispatchToVendorPayload, ): Record { return { WorkOrderIds: payload.workOrderIds, VendorIds: payload.vendorIds, NTEAmount: payload.nteAmount, ScheduledDate: payload.scheduledDate, EstimatedArrivalAt: payload.estimatedArrivalAt, EtaManualOverride: payload.etaManualOverride ?? false, Description: payload.description, TaskListTemplateId: payload.taskListTemplateId, CustomChecklistItems: payload.customChecklistItems, }; } export function mapUpdateDispatchPayload(payload: UpdateDispatchPayload): Record { return { Id: payload.id, Status: payload.status, NTEAmount: payload.nteAmount, ScheduledDate: payload.scheduledDate, EstimatedArrivalAt: payload.estimatedArrivalAt, EtaManualOverride: payload.etaManualOverride ?? false, CompletedDate: payload.completedDate, Description: payload.description, }; } export function mapUpliftRequest(raw: unknown): UpliftRequest { const item = asRecord(raw); return { id: readId(item), status: readString(item, "status", "Status"), currentNTE: readNumber(item, "currentNTE", "CurrentNTE"), requestedNTE: readNumber(item, "requestedNTE", "RequestedNTE") ?? 0, delta: readNumber(item, "delta", "Delta") ?? 0, vendorReason: readString(item, "vendorReason", "VendorReason"), requestedAt: readString(item, "requestedAt", "RequestedAt"), requestedByVendorName: readString(item, "requestedByVendorName", "RequestedByVendorName"), decidedAt: readString(item, "decidedAt", "DecidedAt"), decidedByName: readString(item, "decidedByName", "DecidedByName"), decisionNote: readString(item, "decisionNote", "DecisionNote"), requiredTier: readNumber(item, "requiredTier", "RequiredTier") ?? 1, canDecide: readBool(item, "canDecide", "CanDecide"), }; } export function mapUpliftRequests(rawItems: unknown[]): UpliftRequest[] { return rawItems.map(mapUpliftRequest); }