import { IERPLaborTicketHandler } from "../../types/erp-connector.js"; import { MMReceiveLaborTicket } from "../../services/mm-api-service/types/receive-types.js"; /** * Handles synchronization of labor tickets between MachineMetrics and ERP systems */ export declare class LaborTicketERPSynchronizer { private static readonly CHECKPOINT_SKEW_MS; /** * Prefetch any labor tickets from the failed queue refs so we can remove them from the * retry queue if they are successfully processed in this run. */ private static prefetchFailedLaborTicketQueueRefs; /** * Compute the successful labor ticket refs to remove from the retry queue. */ private static computeSuccessRefsToRemoveFromRetryQueue; /** * Synchronizes updated labor tickets from MachineMetrics to an ERP system. * * Complications (why this is more than "fetch updates and post"): * - MM's /export/updates feed is driven by timestamps (e.g. updatedAt) which may change for reasons * that are not "business-relevant changes" (internal touches or background processes, but especially * our write-back). * - Our own MM write-back (esp. setting laborTicketId on creates) can bump MM timestamps and cause * the same ticket to reappear on the updates feed. * - Connector host time and MM time can differ (clock skew / timestamp precision). A ticket can look * "in the future" relative to the connector host. * - We still need bounded runs and safe checkpointing: updates can arrive mid-run, and the feed can * be reordered, so checkpointing must avoid gaps. * * Solution approach (high level): * - Persist a local SQLite ledger keyed by (system, laborTicketRef) with the last attempted * "business signature" (excludes updatedAt, laborTicketId, etc.). This prevents re-processing * substantially identical tickets across cycles, even if MM timestamps would otherwise allow it. * - Also keep in-run dedupe by a stable business signature so a single run doesn't re-attempt the same * substantive state. * - Use a barrier time + checkpoint clamp for bounded processing and to reduce checkpoint gaps; but * do not skip tickets solely due to "future vs barrier" timestamps (we only log the comparison). * - Provide per-ticket decision logging to explain why each ticket was processed or skipped. */ static syncToERP(connectorType: string, connector: IERPLaborTicketHandler, options?: { onlyClosed?: boolean; }): Promise; private static trackMaxMmTimestampWithinBarrier; private static buildPendingLaborTickets; private static processPendingLaborTickets; private static finalizeRetryQueueForToErp; /** * Retries labor tickets that have failed to be created or updated in the ERP during the sync */ static retryFailed(connectorType: string, connector: IERPLaborTicketHandler): Promise; private static writeLaborTicketIdToMM; private static processLaborTicket; /** * A stable identity for "did the business-relevant contents of this labor ticket change?" * * IMPORTANT: Excludes fields that the SDK itself mutates (e.g. laborTicketId, updatedAt) * AND identity fields the caller already keys the ledger by (laborTicketRef / erp-import key). * Including identity here would make from-ERP imports (no ref yet) and the next to-ERP * read (real MM ref) produce different hashes for the same business state, defeating dedupe. * * PUBLIC: Used by both polling and NATS handlers for consistent deduplication */ static laborTicketBusinessSignature(ticket: MMReceiveLaborTicket): string; /** * Selects a comparable timestamp for windowing decisions. * Prefer updatedAt; fall back to other stable timestamps if needed. */ private static getComparableTicketTimestamp; private static addMsToIso; /** * Computes the checkpoint timestamp for the next barrier by adding the skew milliseconds to the maximum MM timestamp within the barrier. */ private static computeCheckpointTimestamp; } //# sourceMappingURL=labor-ticket-erp-synchronizer.d.ts.map