/** * Response interfaces for MM API calls * * These interfaces provide type safety for the various response shapes * returned by the MM API based on HTTP status codes and entity types. * * Usage pattern: * ```typescript * const result = await client.sendWorkOrdersToMM(orders); * if (result.httpStatus === 200) { * const success = result as MM200NonLaborTicketResponse; * console.log(`Processed ${success.affectedRows} records`); * } else if (result.httpStatus === 207) { * const partial = result as MM207NonLaborTicketResponse; * console.log(`Partial success with ${partial.data.errors.length} error batches`); * } * ``` */ // Base response interface - all responses include httpStatus export interface MMApiBaseResponse { httpStatus: number; } // ============================================================================ // 200 SUCCESS RESPONSES // ============================================================================ /** * Response for successful processing of non-labor ticket entities * Used for: Resources, Parts, PartOperations, WorkOrders, WorkOrderOperations, Persons, Reasons * * Example response: * { * message: "Persons imported successfully", * affectedRows: 10, * httpStatus: 200 * } */ export interface MM200NonLaborTicketResponse extends MMApiBaseResponse { httpStatus: 200; message: string; affectedRows: number; } /** * Response for successful processing of labor tickets * * Example response: * { * message: "Labor tickets imported successfully", * updated: 1, * inserted: 0, * httpStatus: 200 * } */ export interface MM200LaborTicketResponse extends MMApiBaseResponse { httpStatus: 200; message: string; updated: number; inserted: number; } // ============================================================================ // 207 PARTIAL SUCCESS RESPONSES // ============================================================================ /** * Response for partial success of non-labor ticket entities * Used for: Resources, Parts, PartOperations, WorkOrders, WorkOrderOperations, Persons, Reasons * * Note: The API may internally re-batch client submissions. Each error represents * a batch that failed due to at least one record having issues. * * Example response: * { * message: "WorkOrderOperations partially imported with errors", * data: { * successfulBatches: 1, * totalBatches: 3, * affectedRows: 1, * errors: [ * { * message: "GraphQL Error: Foreign key violation...", * batch: 1, * path: "$.selectionSet.insertErpWorkOrderOperations.args.objects", * code: "constraint-violation", * batchData: [{ workOrderId: "24-0196", ... }] // All records in failing batch * } * ] * }, * httpStatus: 207 * } */ export interface MM207NonLaborTicketResponse extends MMApiBaseResponse { httpStatus: 207; message: string; data: { successfulBatches: number; totalBatches: number; affectedRows: number; errors: Array<{ message: string; batch: number; path: string; code: string; batchData: Record[]; // Complete batch data where at least one record failed }>; }; } /** * Response for partial success of labor tickets * * Note: The API may internally re-batch client submissions. Each error represents * a batch that failed due to at least one record having issues. * * Example response: * { * message: "Labor tickets partially imported with errors", * data: { * updated: 1, * inserted: 0, * updateErrors: [ * { * message: "GraphQL Error: Foreign key violation...", * batch: 1, * path: "$.selectionSet.updateErpLaborTickets.args.objects", * code: "constraint-violation", * batchData: [{ workOrderId: "24-0196", ... }] // All records in failing batch * } * ], * insertErrors: [ * { * message: "GraphQL Error: Foreign key violation...", * batch: 1, * path: "$.selectionSet.insertErpLaborTickets.args.objects", * code: "constraint-violation", * batchData: [{ workOrderId: "24-0191", ... }] // All records in failing batch * } * ] * }, * httpStatus: 207 * } */ export interface MM207LaborTicketResponse extends MMApiBaseResponse { httpStatus: 207; message: string; data: { updated: number; inserted: number; updateErrors: Array<{ message: string; batch: number; path: string; code: string; batchData: Record[]; // Complete batch data where at least one record failed }>; insertErrors: Array<{ message: string; batch: number; path: string; code: string; batchData: Record[]; // Complete batch data where at least one record failed }>; }; } // ============================================================================ // 500 EXCEPTION STRUCTURES // ============================================================================ /** * Exception structure for complete failure of non-labor ticket entities * Used for: Resources, Parts, PartOperations, WorkOrders, WorkOrderOperations, Persons, Reasons * * IMPORTANT: This structure only applies to 500 errors caused by data validation failures. * Other 500 errors (network issues, server errors, etc.) may have different structures. * This structure appears in caught exceptions when the API returns 500 status * specifically for validation issues that contain structured error information. * * Note: The API may internally re-batch client submissions. Each error represents * a batch that failed due to at least one record having issues. * * Example exception.data structure: * { * error: "Failed to import WorkOrderOperations", * message: { * successfulBatches: 0, * totalBatches: 1, * affectedRows: 0, * errors: [ * { * message: "GraphQL Error: Foreign key violation...", * batchData: [{ workOrderId: "24-0196", ... }] // All records in failing batch * } * ] * } * } */ export interface MM500NonLaborTicketException { status: 500; data: { error: string; message: { successfulBatches: number; totalBatches: number; affectedRows: number; errors: Array<{ message: string; batchData: Record[]; // Complete batch data where at least one record failed }>; }; }; } /** * Exception structure for complete failure of labor tickets * * IMPORTANT: This structure only applies to 500 errors caused by data validation failures. * Other 500 errors (network issues, server errors, etc.) may have different structures. * This structure appears in caught exceptions when the API returns 500 status * specifically for validation issues that contain structured error information. * * Note: The API may internally re-batch client submissions. Each error represents * a batch that failed due to at least one record having issues. * * Example exception.data structure: * { * error: "Failed to import LaborTickets", * message: { * updateErrors: [ * { * message: "GraphQL Error: Foreign key violation...", * batchData: [{ workOrderId: "24-0196", ... }] // All records in failing batch * } * ], * insertErrors: [ * { * message: "GraphQL Error: Foreign key violation...", * batchData: [{ workOrderId: "24-0191", ... }] // All records in failing batch * } * ] * } * } */ export interface MM500LaborTicketException { status: 500; data: { error: string; message: { updateErrors: Array<{ message: string; batchData: Record[]; // Complete batch data where at least one record failed }>; insertErrors: Array<{ message: string; batchData: Record[]; // Complete batch data where at least one record failed }>; }; }; } // ============================================================================ // SPECIALIZED RESPONSES - WELL-DEFINED STRUCTURES // ============================================================================ /** * Response for GraphQL reason-by-ref query * Used by fetchReasonByRef() to look up reasonId/code from reasonRef */ export interface MMGraphQLReasonResponse extends MMApiBaseResponse { data: { erpReasons: Array<{ annotationTypeRef: string | null; category: string; code: string; companyId: string; createdAt: string; description: string; entityType: string; reasonId: string; reasonRef: number; rejectReasonRef: number; updatedAt: string | null; }>; }; } /** * Response for GraphQL work order queries * Intended for use by fetchWorkOrderFromGraphQL() method */ export interface MMGraphQLWorkOrderResponse extends MMApiBaseResponse { data: { workOrder: MMGraphQLWorkOrderRow[]; }; } /** * One work order row as returned by the MM GraphQL `erpWorkOrders` query. * Shared by the single-fetch and paged-fetch queries, which request the same field list. */ export interface MMGraphQLWorkOrderRow { companyId: string; workOrderId: string; lot: string; split: string; sub: string; status: string; dueDate: string | null; description: string; scheduledStartDate: string | null; scheduledEndDate: string | null; closedDate: string | null; quantityRequired: number | string; partNumber: string; partRevision: string; method: string; createdAt: string; updatedAt: string; customFieldsSchemaRef: number | null; customFields: Record | null; } /** * Response for the paged GraphQL work orders query * Used by fetchAllErpWorkOrdersFromGraphQL() */ export interface MMGraphQLWorkOrdersPageResponse extends MMApiBaseResponse { data: { workOrders: MMGraphQLWorkOrderRow[]; }; } /** * One row of the erpWorkOrderOperations GraphQL query. Hasura serializes `bigint` * columns as strings and `Float`/`numeric` columns as numbers or strings depending on * the value, so every numeric field is typed as `number | string` and coerced by the * caller. */ export interface MMGraphQLWorkOrderOperationRow { companyId: string; workOrderId: string; lot: string; split: string; sub: string; sequenceNumber: number | string; resourceId: string; startQuantity: number | string | null; finishQuantity: number | string | null; expectedRejectRate: number | string | null; scheduledStartDate: string | null; scheduledFinishDate: string | null; closedDate: string | null; cycleTimeMs: number | string | null; setupTimeMs: number | string | null; productionburdenRateHourly: number | string | null; setupburdenRatehourly: number | string | null; operationType: string; quantityPerPart: number | string | null; status: string; createdAt: string; updatedAt: string; customFieldsSchemaRef: number | null; customFields: Record | null; } /** * Response for the paged GraphQL work order operations query * Used by fetchAllErpWorkOrderOperationsFromGraphQL() */ export interface MMGraphQLWorkOrderOperationsPageResponse extends MMApiBaseResponse { data: { workOrderOperations: MMGraphQLWorkOrderOperationRow[]; }; } /** * ERP-side resources mapped to MM machines (`machineRef` present). Typically sourced from MES * `/mes/settings/machine-settings` (`laborResourceId` maps to {@link resourceId}). */ export interface MMMappedErpResourceRow { isResourceGroup: boolean; machineGroupId: string | null; machineRef: number; /** ERP labor resource id from MES machine settings (`laborResourceId`). */ resourceId: string | null; /** Display name when returned by MES; otherwise null (callers may default for UI). */ name: string | null; type: string | null; } /** * One time bucket row from GraphQL `aggregatedPartCounts` for a single `machineRef` and `metricKey`. * Window and bucket shape follow the MM GraphQL schema (see MachineMetrics GraphQL docs). */ export interface MMAggregatedPartCountBucket { machineRef: number; metricKey: string; /** Sum of metric values across events in this bucket. */ sumOfValues: number; /** Number of contributing events in this bucket. */ eventCount: number; bucketStartAt: string; bucketEndAt: string; /** Duration string for this bucket when the API provides it (may be empty). */ bucketWidth: string; /** Optional gauge at bucket start; null when not applicable. */ firstValue: number | null; /** Optional gauge at bucket end; null when not applicable. */ lastValue: number | null; } /** * Response for machine groups fetch * Used by fetchMachineGroups() method */ export interface MMMachineGroupsResponse extends MMApiBaseResponse { data: Array<{ id: string; machines: Array<{ machineRef: string; }>; }>; } // ============================================================================ // NOTES ON OTHER SPECIALIZED RESPONSES // ============================================================================ /** * For other operations like saveCheckpoint, deleteLaborTickets, etc., * we return Promise until we have: * * 1. Clear understanding of actual response shapes * 2. Real need for type safety on those responses * 3. Actual usage patterns that would benefit from specific typing * * This avoids the anti-pattern of vague interfaces with [key: string]: unknown * that provide no real type safety. */ /** * One row of the `erpWorkOrderOperationDispatchPriorities` GraphQL query. Hasura renders * `bigint` as string or number depending on configuration and `Float` as number or string * depending on the value; `parseDispatchPriorityRow` coerces both. */ export interface MMGraphQLDispatchPriorityRow { dispatchPriorityRef: number | string; companyId: string; authorityOperationId: string | null; scopeResourceId: string; workOrderId: string; sequenceNumber: number | string; lot: string; split: string; sub: string; preallocatedMachineResourceId: string | null; priority: number | string; createdAt: string; updatedAt: string; customFieldsSchemaRef: number | string | null; customFields: Record | null; } /** Response for the paged distinct-scopes query (`rows` alias). */ export interface MMGraphQLDispatchPriorityScopesPageResponse extends MMApiBaseResponse { data: { rows: { scopeResourceId: string }[]; }; } /** Response for the paged dispatch-priorities query (`rows` alias). */ export interface MMGraphQLDispatchPrioritiesPageResponse extends MMApiBaseResponse { data: { rows: MMGraphQLDispatchPriorityRow[]; }; }