import type { ErpWorkOrderKey, ErpWorkOrderOperationKey, IERPWorkOrderReconciliationHandler } from "../../types/erp-connector.js"; import type { MMReceiveWorkOrder, MMReceiveWorkOrderOperation } from "../../services/mm-api-service/types/receive-types.js"; import { MMSendWorkOrderOperationStatusPatch, MMSendWorkOrderStatusPatch } from "../../services/mm-api-service/types/send-types.js"; import type { WriteEntitiesToMMResult } from "./standard-process-drivers.js"; /** * MM statuses eligible for deletion reconciliation, for work orders and work order * operations alike. Only rows in these statuses are compared against the ERP; everything * else (COMPLETED, CANCELLED, already DELETED, etc.) is left untouched. Values are in the * canonical form produced by {@link normalizeMMStatus}. */ export declare const RECONCILE_CANDIDATE_STATUSES: ReadonlySet; export interface ReconcileDeletedWorkOrdersOptions { /** * Abort when more than this fraction of candidates would be deleted * (guard against a bad/partial ERP fetch mass-deleting jobs). Evaluated separately * for the work order set and the work order operation set. * Env fallback `RECONCILE_MAX_DELETE_FRACTION`; default 0.2. */ maxDeleteFraction?: number; /** * The fraction guard only applies when the deletion count exceeds this floor, * so small shops aren't blocked from legitimately deleting a handful of jobs. * Env fallback `RECONCILE_DELETE_GUARD_MIN_COUNT`; default 10. */ deleteGuardMinCount?: number; /** * Log what would be marked DELETED without writing to MM. * Env fallback `RECONCILE_WORK_ORDERS_DRY_RUN`; default false. */ dryRun?: boolean; /** Rows per MM GraphQL page when fetching work orders / operations (default 500). */ pageSize?: number; } export type ReconcileDeletedWorkOrdersStatus = "completed" | "dry-run"; export interface ReconcileDeletedWorkOrdersResult { status: ReconcileDeletedWorkOrdersStatus; /** * Only set on "dry-run" results whose deletion set would have tripped the * blast-radius guard on a real run; absent otherwise. */ reason?: string; /** * MM work orders in a candidate status (RELEASED / ACTIVE, case-insensitive). */ candidateCount: number; /** Composite keys identified for deletion. */ workOrdersToDelete: ErpWorkOrderKey[]; /** * Size of the work order deletion set applied to MM (0 unless status is "completed"). * Counts rows the dedupe caches skipped as already up to date too — the MM write * throws on any failure, so when this is returned the whole set is DELETED in MM. */ markedDeletedCount: number; /** * Whether the handler implements `selectWorkOrderOperationsToDelete()`. When false, * operations deleted from surviving jobs are not reconciled (the parent-deletion * cascade below still runs). */ workOrderOperationsReconciled: boolean; /** * MM work order operations eligible for the handler: status RELEASED / ACTIVE, no * `closedDate`, parent work order a live candidate. Computed even when the handler * does not implement `selectWorkOrderOperationsToDelete()` (see * `workOrderOperationsReconciled`), so a connector can see how many operations it * would be offered by opting in. */ workOrderOperationCandidateCount: number; /** Operation keys the handler selected for deletion (ops gone from surviving jobs). */ workOrderOperationsToDelete: ErpWorkOrderOperationKey[]; /** * Operation keys marked DELETED because their parent work order is DELETED in MM * (by this run or an earlier one). Not counted against the blast-radius guard: the * parent's deletion was already guarded. */ cascadedWorkOrderOperationsToDelete: ErpWorkOrderOperationKey[]; /** * Size of the operation deletion set (selected + cascaded) applied to MM * (0 unless status is "completed"). */ markedDeletedWorkOrderOperationCount: number; } /** Which reconciliation phase a {@link ReconcileDeletedWorkOrdersAbortedError} refers to. */ export type ReconcileEntity = "workOrders" | "workOrderOperations"; /** * Thrown when the blast-radius guard refuses to mark the computed deletion set as * DELETED (typically a bad/partial ERP fetch). Nothing is written for the aborted * phase; the next scheduled cycle retries. Note that when `entity` is * `workOrderOperations`, the work order phase of the same run has already completed * (its writes are idempotent and stand). Mirrors the SDK convention of typed exceptions * for abnormal outcomes (cf. {@code MMBatchValidationError}). */ export declare class ReconcileDeletedWorkOrdersAbortedError extends Error { readonly entity: ReconcileEntity; readonly candidateCount: number; /** Populated when `entity` is `workOrders`; empty otherwise. */ readonly workOrdersToDelete: ErpWorkOrderKey[]; /** Populated when `entity` is `workOrderOperations`; empty otherwise. */ readonly workOrderOperationsToDelete: ErpWorkOrderOperationKey[]; readonly deleteFraction: number; readonly maxDeleteFraction: number; readonly deleteGuardMinCount: number; constructor(options: { message: string; entity?: ReconcileEntity; candidateCount: number; workOrdersToDelete?: ErpWorkOrderKey[]; workOrderOperationsToDelete?: ErpWorkOrderOperationKey[]; deleteFraction: number; maxDeleteFraction: number; deleteGuardMinCount: number; }); } /** Injection seam for tests; production callers omit it. @internal */ export interface WorkOrderReconcilerDeps { fetchAllWorkOrders: (pageSize?: number) => Promise; writeWorkOrders: (records: MMSendWorkOrderStatusPatch[], connectorType: string) => Promise; fetchAllWorkOrderOperations: (pageSize?: number) => Promise; writeWorkOrderOperations: (records: MMSendWorkOrderOperationStatusPatch[], connectorType: string) => Promise; } /** * Builds the canonical composite key string for a work order identity. * Case-insensitive and whitespace-tolerant so key-cosmetic differences between the * ERP fetch and MM's stored values never produce a false deletion. */ export declare const normalizeWorkOrderKey: (key: ErpWorkOrderKey) => string; /** * Builds the canonical composite key string for a work order operation identity: * {@link normalizeWorkOrderKey} plus the sequence number (numeric-canonical, so "010", * "10.0", 10 and "10" all match). */ export declare const normalizeWorkOrderOperationKey: (key: ErpWorkOrderOperationKey) => string; /** * True when the status is eligible for deletion reconciliation (case-insensitive, * see {@link normalizeMMStatus}). */ export declare const isReconcileCandidateStatus: (status: string | null | undefined) => boolean; /** * True when a work order operation is eligible for deletion reconciliation on its own * account: candidate status and not closed. (Parent work order liveness is checked by * the reconciler, which has the work order set at hand.) */ export declare const isReconcileCandidateWorkOrderOperation: (operation: Pick) => boolean; /** * Matching helper for the common {@code selectWorkOrdersToDelete()} implementation: * returns the candidates whose composite key is absent from `activeErpKeys` * (trimmed, case-insensitive — see {@link normalizeWorkOrderKey}). * * Guards against handler bugs instead of diffing garbage: throws when `activeErpKeys` * is not an array or contains a key without a usable `workOrderId` (e.g. a * wrong-field-name mapping bug would mangle EVERY key, empty the effective active set, * and on a small candidate set wrongly delete jobs below the blast-radius floor). */ export declare const selectWorkOrdersMissingFromErp: (candidates: MMReceiveWorkOrder[], activeErpKeys: ErpWorkOrderKey[]) => MMReceiveWorkOrder[]; /** * Work order operation counterpart of {@link selectWorkOrdersMissingFromErp}: returns * the candidate operations whose composite key (work order key + `sequenceNumber`) is * absent from `activeErpKeys`. Throws on a non-array or on keys lacking a usable * `workOrderId` or `sequenceNumber` (non-blank string or finite number). */ export declare const selectWorkOrderOperationsMissingFromErp: (candidates: MMReceiveWorkOrderOperation[], activeErpKeys: ErpWorkOrderOperationKey[]) => MMReceiveWorkOrderOperation[]; export declare class WorkOrderReconciler { /** * Marks MM work orders — and their operations — as DELETED when the connector no * longer reports them as active in the ERP. See * {@code StandardProcessDrivers.reconcileDeletedWorkOrders} for the public entry point * and contract details. * * Two phases, work orders first: * 1. Work orders: candidates (RELEASED / ACTIVE) → handler selection → guard → write. * 2. Work order operations: (a) operations of work orders DELETED in MM (by this run * or earlier) are cascaded to DELETED without consulting the handler or the guard; * (b) open RELEASED / ACTIVE operations of surviving candidate work orders are * offered to `selectWorkOrderOperationsToDelete()` when implemented, guarded, and * written together with the cascade set. */ static reconcileDeleted(connectorType: string, handler: IERPWorkOrderReconciliationHandler, options?: ReconcileDeletedWorkOrdersOptions, deps?: WorkOrderReconcilerDeps): Promise; private static resolveGuardSettings; /** * Operation phase. Fetches all operations from MM, cascades those of deleted parents, * offers the open operations of live parents to the handler (when implemented), and — * unless dry-running — writes the union as DELETED. */ private static reconcileOperations; } //# sourceMappingURL=work-order-reconciler.d.ts.map