@machinemetrics/mm-erp-sdk 0.3.0-beta.0 → 0.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/services/data-sync-service/configuration-manager.d.ts.map +1 -1
  6. package/dist/services/data-sync-service/configuration-manager.js +30 -30
  7. package/dist/services/data-sync-service/configuration-manager.js.map +1 -1
  8. package/dist/services/data-sync-service/data-sync-service.d.ts.map +1 -1
  9. package/dist/services/data-sync-service/data-sync-service.js +9 -0
  10. package/dist/services/data-sync-service/data-sync-service.js.map +1 -1
  11. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.d.ts.map +1 -1
  12. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.js +1 -2
  13. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.js.map +1 -1
  14. package/dist/services/data-sync-service/jobs/from-erp.d.ts.map +1 -1
  15. package/dist/services/data-sync-service/jobs/from-erp.js +7 -13
  16. package/dist/services/data-sync-service/jobs/from-erp.js.map +1 -1
  17. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.d.ts.map +1 -1
  18. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.js +1 -2
  19. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.js.map +1 -1
  20. package/dist/services/data-sync-service/jobs/run-migrations.d.ts.map +1 -1
  21. package/dist/services/data-sync-service/jobs/run-migrations.js +1 -2
  22. package/dist/services/data-sync-service/jobs/run-migrations.js.map +1 -1
  23. package/dist/services/data-sync-service/jobs/to-erp.d.ts.map +1 -1
  24. package/dist/services/data-sync-service/jobs/to-erp.js +12 -3
  25. package/dist/services/data-sync-service/jobs/to-erp.js.map +1 -1
  26. package/dist/services/data-sync-service/nats-labor-ticket-listener.d.ts +30 -0
  27. package/dist/services/data-sync-service/nats-labor-ticket-listener.d.ts.map +1 -0
  28. package/dist/services/data-sync-service/nats-labor-ticket-listener.js +290 -0
  29. package/dist/services/data-sync-service/nats-labor-ticket-listener.js.map +1 -0
  30. package/dist/services/mm-api-service/company-info.d.ts +13 -0
  31. package/dist/services/mm-api-service/company-info.d.ts.map +1 -0
  32. package/dist/services/mm-api-service/company-info.js +60 -0
  33. package/dist/services/mm-api-service/company-info.js.map +1 -0
  34. package/dist/services/mm-api-service/index.d.ts +7 -0
  35. package/dist/services/mm-api-service/index.d.ts.map +1 -1
  36. package/dist/services/mm-api-service/index.js +5 -0
  37. package/dist/services/mm-api-service/index.js.map +1 -1
  38. package/dist/services/mm-api-service/mm-api-service.d.ts +6 -0
  39. package/dist/services/mm-api-service/mm-api-service.d.ts.map +1 -1
  40. package/dist/services/mm-api-service/mm-api-service.js +15 -3
  41. package/dist/services/mm-api-service/mm-api-service.js.map +1 -1
  42. package/dist/services/mm-api-service/types/receive-types.d.ts +3 -0
  43. package/dist/services/mm-api-service/types/receive-types.d.ts.map +1 -1
  44. package/dist/services/mm-api-service/types/receive-types.js +1 -0
  45. package/dist/services/mm-api-service/types/receive-types.js.map +1 -1
  46. package/dist/services/mm-api-service/types/send-types.d.ts +17 -8
  47. package/dist/services/mm-api-service/types/send-types.d.ts.map +1 -1
  48. package/dist/services/mm-api-service/types/send-types.js +41 -17
  49. package/dist/services/mm-api-service/types/send-types.js.map +1 -1
  50. package/dist/services/nats-service/nats-service.d.ts +114 -0
  51. package/dist/services/nats-service/nats-service.d.ts.map +1 -0
  52. package/dist/services/nats-service/nats-service.js +244 -0
  53. package/dist/services/nats-service/nats-service.js.map +1 -0
  54. package/dist/services/nats-service/test-nats-subscriber.d.ts +6 -0
  55. package/dist/services/nats-service/test-nats-subscriber.d.ts.map +1 -0
  56. package/dist/services/nats-service/test-nats-subscriber.js +79 -0
  57. package/dist/services/nats-service/test-nats-subscriber.js.map +1 -0
  58. package/dist/services/reporting-service/logger.d.ts.map +1 -1
  59. package/dist/services/reporting-service/logger.js +31 -6
  60. package/dist/services/reporting-service/logger.js.map +1 -1
  61. package/dist/types/erp-connector.d.ts +1 -8
  62. package/dist/types/erp-connector.d.ts.map +1 -1
  63. package/dist/types/index.d.ts +0 -1
  64. package/dist/types/index.d.ts.map +1 -1
  65. package/dist/utils/error-formatter.d.ts +19 -0
  66. package/dist/utils/error-formatter.d.ts.map +1 -0
  67. package/dist/utils/error-formatter.js +184 -0
  68. package/dist/utils/error-formatter.js.map +1 -0
  69. package/dist/utils/http-client.js +2 -4
  70. package/dist/utils/http-client.js.map +1 -1
  71. package/dist/utils/index.d.ts +5 -1
  72. package/dist/utils/index.d.ts.map +1 -1
  73. package/dist/utils/index.js +4 -1
  74. package/dist/utils/index.js.map +1 -1
  75. package/dist/utils/local-data-store/jobs-shared-data.d.ts +0 -2
  76. package/dist/utils/local-data-store/jobs-shared-data.d.ts.map +1 -1
  77. package/dist/utils/local-data-store/jobs-shared-data.js +0 -2
  78. package/dist/utils/local-data-store/jobs-shared-data.js.map +1 -1
  79. package/dist/utils/mm-labor-ticket-helpers.d.ts +4 -3
  80. package/dist/utils/mm-labor-ticket-helpers.d.ts.map +1 -1
  81. package/dist/utils/mm-labor-ticket-helpers.js +7 -12
  82. package/dist/utils/mm-labor-ticket-helpers.js.map +1 -1
  83. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.d.ts +0 -15
  84. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.d.ts.map +1 -1
  85. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.js +46 -180
  86. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.js.map +1 -1
  87. package/dist/utils/standard-process-drivers/mm-entity-processor.d.ts +1 -7
  88. package/dist/utils/standard-process-drivers/mm-entity-processor.d.ts.map +1 -1
  89. package/dist/utils/standard-process-drivers/mm-entity-processor.js +1 -7
  90. package/dist/utils/standard-process-drivers/mm-entity-processor.js.map +1 -1
  91. package/dist/utils/standard-process-drivers/standard-process-drivers.d.ts +2 -8
  92. package/dist/utils/standard-process-drivers/standard-process-drivers.d.ts.map +1 -1
  93. package/dist/utils/standard-process-drivers/standard-process-drivers.js +18 -27
  94. package/dist/utils/standard-process-drivers/standard-process-drivers.js.map +1 -1
  95. package/dist/utils/time-utils.d.ts.map +1 -1
  96. package/dist/utils/time-utils.js +0 -7
  97. package/dist/utils/time-utils.js.map +1 -1
  98. package/package.json +5 -4
  99. package/src/index.ts +3 -0
  100. package/src/services/data-sync-service/configuration-manager.ts +37 -50
  101. package/src/services/data-sync-service/data-sync-service.ts +10 -0
  102. package/src/services/data-sync-service/jobs/clean-up-expired-cache.ts +1 -2
  103. package/src/services/data-sync-service/jobs/from-erp.ts +7 -13
  104. package/src/services/data-sync-service/jobs/retry-failed-labor-tickets.ts +1 -2
  105. package/src/services/data-sync-service/jobs/run-migrations.ts +1 -2
  106. package/src/services/data-sync-service/jobs/to-erp.ts +12 -3
  107. package/src/services/data-sync-service/nats-labor-ticket-listener.ts +342 -0
  108. package/src/services/mm-api-service/company-info.ts +87 -0
  109. package/src/services/mm-api-service/index.ts +8 -0
  110. package/src/services/mm-api-service/mm-api-service.ts +20 -3
  111. package/src/services/mm-api-service/types/receive-types.ts +1 -0
  112. package/src/services/mm-api-service/types/send-types.ts +40 -16
  113. package/src/services/nats-service/nats-service.ts +351 -0
  114. package/src/services/nats-service/test-nats-subscriber.ts +96 -0
  115. package/src/services/reporting-service/logger.ts +39 -7
  116. package/src/types/erp-connector.ts +1 -8
  117. package/src/types/index.ts +0 -8
  118. package/src/utils/error-formatter.ts +205 -0
  119. package/src/utils/http-client.ts +3 -4
  120. package/src/utils/index.ts +6 -5
  121. package/src/utils/local-data-store/jobs-shared-data.ts +0 -2
  122. package/src/utils/mm-labor-ticket-helpers.ts +8 -11
  123. package/src/utils/standard-process-drivers/labor-ticket-erp-synchronizer.ts +64 -220
  124. package/src/utils/standard-process-drivers/mm-entity-processor.ts +1 -7
  125. package/src/utils/standard-process-drivers/standard-process-drivers.ts +19 -33
  126. package/src/utils/time-utils.ts +0 -11
  127. package/dist/types/flattened-work-order.d.ts +0 -99
  128. package/dist/types/flattened-work-order.d.ts.map +0 -1
  129. package/dist/types/flattened-work-order.js +0 -2
  130. package/dist/types/flattened-work-order.js.map +0 -1
  131. package/dist/utils/env.d.ts +0 -8
  132. package/dist/utils/env.d.ts.map +0 -1
  133. package/dist/utils/env.js +0 -58
  134. package/dist/utils/env.js.map +0 -1
  135. package/dist/utils/erp-timezone-utils.d.ts +0 -20
  136. package/dist/utils/erp-timezone-utils.d.ts.map +0 -1
  137. package/dist/utils/erp-timezone-utils.js +0 -75
  138. package/dist/utils/erp-timezone-utils.js.map +0 -1
  139. package/src/types/flattened-work-order.ts +0 -108
  140. package/src/utils/env.ts +0 -75
  141. package/src/utils/erp-timezone-utils.ts +0 -99
@@ -1,11 +1,9 @@
1
1
  import { createLogger, format, transports } from "winston";
2
2
  import DailyRotateFile from "winston-daily-rotate-file";
3
3
  import path from "path";
4
- import { getEnvString } from "../../utils/env.js"; // Utility function to get environment variables with default values and validation
5
4
 
6
5
  const logDirectory = "logs";
7
- const initialLogLevel = getEnvString("LOG_LEVEL", "info");
8
- const initialNodeEnv = getEnvString("NODE_ENV", "development");
6
+ const initialLogLevel = process.env.LOG_LEVEL || "info";
9
7
 
10
8
  const LOGGER_ERROR_PREFIX = "[mm-erp-sdk logger]";
11
9
 
@@ -52,8 +50,45 @@ const truncateString = (str: string): string => {
52
50
  return result;
53
51
  };
54
52
 
53
+ /**
54
+ * Safe JSON serializer that handles circular references and extracts meaningful error data
55
+ */
56
+ const safeStringify = (obj: any, indent: number = 2): string => {
57
+ const seen = new WeakSet();
58
+
59
+ return JSON.stringify(
60
+ obj,
61
+ (key, value) => {
62
+ // Handle Error objects specially
63
+ if (value instanceof Error) {
64
+ return {
65
+ message: value.message,
66
+ name: value.name,
67
+ stack: value.stack,
68
+ ...(value.constructor.name === 'HTTPError' || value.constructor.name === 'AxiosError' ? {
69
+ status: (value as any).status,
70
+ code: (value as any).code,
71
+ data: (value as any).data,
72
+ } : {}),
73
+ };
74
+ }
75
+
76
+ // Handle circular references
77
+ if (typeof value === "object" && value !== null) {
78
+ if (seen.has(value)) {
79
+ return "[Circular]";
80
+ }
81
+ seen.add(value);
82
+ }
83
+
84
+ return value;
85
+ },
86
+ indent
87
+ );
88
+ };
89
+
55
90
  const baseFormat = format.printf(({ timestamp, level, message, ...rest }) => {
56
- let restString = JSON.stringify(rest, undefined, 2);
91
+ let restString = safeStringify(rest, 2);
57
92
  restString = restString === "{}" ? "" : restString;
58
93
 
59
94
  let formattedMessage: string;
@@ -242,9 +277,6 @@ export const configureLogger = (logLevel: string, nodeEnv: string) => {
242
277
  logger.level = logLevel;
243
278
  };
244
279
 
245
- // Ensure file logging is configured as soon as the module loads
246
- configureLogger(initialLogLevel, initialNodeEnv);
247
-
248
280
  // Add fatal method for Graceful compatibility
249
281
  (logger as any).fatal = (err: any, meta?: any) => logger.error(err, meta);
250
282
 
@@ -14,10 +14,6 @@ export interface IERPConnector {
14
14
 
15
15
  /**
16
16
  * Sync data from the ERP system to MM.
17
- * Timezone expectations:
18
- * - When sending ERP data to MachineMetrics (e.g., via StandardProcessDrivers or MMEntityProcessor),
19
- * connectors MUST convert all datetime fields to ISO-8601 UTC strings (trailing `Z` or explicit offset)
20
- * before invoking SDK helpers. The SDK validates/forwards these values but does not apply timezone shifts.
21
17
  */
22
18
  syncFromERP(): Promise<void>;
23
19
  syncFromERPCompleted(): Promise<void>;
@@ -35,16 +31,13 @@ export interface IERPConnector {
35
31
  retryFailedLaborTicketsCompleted(): Promise<void>;
36
32
 
37
33
  /**
38
- * Start the connector. Typically a connector should delegate control to the data-sync-service via runDataSyncService().
34
+ * Start the connector. Typically a connector should delegate control to the data-sync-service.
39
35
  */
40
36
  startUp(): Promise<void>;
41
37
  }
42
38
 
43
39
  /**
44
40
  * Interface for standard process methods that call ERP connectors back to create or update labor tickets.
45
- *
46
- * Timestamp fields on `MMReceiveLaborTicket` objects have already been converted by the SDKfrom MM's
47
- * UTC API responses into the ERP's local timezone, based on the company's timezone setting.
48
41
  */
49
42
  export interface IERPLaborTicketHandler {
50
43
  createLaborTicketInERP(
@@ -10,11 +10,3 @@ export type {
10
10
  ERPResponse,
11
11
  ERPObject,
12
12
  } from "./erp-types.js";
13
- export type {
14
- FlattenedWorkOrderRow,
15
- FlattenedWorkOrderRowBase,
16
- FlattenedWorkOrderPartFields,
17
- FlattenedWorkOrderPartOperationFields,
18
- FlattenedWorkOrderFields,
19
- FlattenedWorkOrderOperationFields,
20
- } from "./flattened-work-order.js";
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Error formatter utility for standardizing error messages across connectors
3
+ * Extracts human-readable messages from various error types (axios, custom errors, etc.)
4
+ */
5
+
6
+ export interface FormattedError {
7
+ message: string;
8
+ code: string;
9
+ httpStatus?: number;
10
+ metadata?: Record<string, any>;
11
+ }
12
+
13
+ /**
14
+ * Extract meaningful error message from various error types
15
+ */
16
+ export function formatError(error: any): FormattedError {
17
+ if (!error) {
18
+ return {
19
+ message: 'Unknown error occurred',
20
+ code: 'UNKNOWN_ERROR',
21
+ };
22
+ }
23
+
24
+ // Handle axios errors FIRST (before checking for FormattedError)
25
+ // because axios errors also have message and code properties
26
+ if (error.isAxiosError || error.name === 'AxiosError') {
27
+ return formatAxiosError(error);
28
+ }
29
+
30
+ // If it's already a FormattedError, return as-is
31
+ // Check for httpStatus or metadata to distinguish from axios errors
32
+ if (error.message && error.code && typeof error.message === 'string' && typeof error.code === 'string' && !error.config) {
33
+ return error as FormattedError;
34
+ }
35
+
36
+ // Handle standard Error objects
37
+ if (error instanceof Error) {
38
+ return {
39
+ message: error.message || 'An error occurred',
40
+ code: 'ERROR',
41
+ metadata: {
42
+ name: error.name,
43
+ },
44
+ };
45
+ }
46
+
47
+ // Handle string errors
48
+ if (typeof error === 'string') {
49
+ return {
50
+ message: error,
51
+ code: 'ERROR',
52
+ };
53
+ }
54
+
55
+ // Last resort: try to extract any useful info
56
+ const message = error.message || error.toString?.() || 'Unknown error occurred';
57
+ return {
58
+ message: typeof message === 'string' ? message : JSON.stringify(message),
59
+ code: error.code || 'UNKNOWN_ERROR',
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Format axios-specific errors with detailed context
65
+ */
66
+ function formatAxiosError(error: any): FormattedError {
67
+ const httpStatus = error.response?.status;
68
+ const method = error.config?.method?.toUpperCase();
69
+ const url = error.config?.url;
70
+
71
+ // First check if connector already extracted the message
72
+ let message = error._extractedMessage;
73
+
74
+ // If not, try to extract from response data
75
+ if (!message) {
76
+ const extractedMessage = extractErrorMessage(error.response?.data);
77
+ message = extractedMessage || error.response?.statusText || error.message || 'Request failed';
78
+ }
79
+
80
+ // Determine error code based on status
81
+ const code = categorizeHttpError(httpStatus);
82
+
83
+ const metadata: Record<string, any> = {
84
+ method,
85
+ url,
86
+ };
87
+
88
+ // Add additional context for specific error types
89
+ if (httpStatus === 401 || httpStatus === 403) {
90
+ metadata.hint = 'Check authentication credentials';
91
+ } else if (httpStatus === 404) {
92
+ metadata.hint = 'Resource not found - check endpoint URL';
93
+ } else if (httpStatus && httpStatus >= 500) {
94
+ metadata.hint = 'ERP system may be temporarily unavailable';
95
+ }
96
+
97
+ return {
98
+ message,
99
+ code,
100
+ httpStatus,
101
+ metadata,
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Extract meaningful error message from response data
107
+ * Handles various ERP-specific response formats
108
+ */
109
+ function extractErrorMessage(data: any): string | null {
110
+ if (!data) return null;
111
+
112
+ // Common error message fields across different ERPs
113
+ const possibleFields = [
114
+ 'ErrorMessage', // Epicor
115
+ 'error.message', // Common REST format
116
+ 'error', // Simple format
117
+ 'Message', // .NET style
118
+ 'message', // JavaScript style
119
+ 'errorMessage', // Camel case
120
+ 'error_message', // Snake case
121
+ 'errors[0].message', // Array of errors
122
+ 'title', // Problem details format
123
+ 'detail', // Problem details format
124
+ ];
125
+
126
+ for (const field of possibleFields) {
127
+ const value = getNestedValue(data, field);
128
+ if (value && typeof value === 'string') {
129
+ return value;
130
+ }
131
+ }
132
+
133
+ // If data is a string, return it
134
+ if (typeof data === 'string') {
135
+ // Try to parse as JSON first
136
+ try {
137
+ const parsed = JSON.parse(data);
138
+ return extractErrorMessage(parsed);
139
+ } catch {
140
+ return data;
141
+ }
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ /**
148
+ * Get nested value from object using dot notation
149
+ */
150
+ function getNestedValue(obj: any, path: string): any {
151
+ const parts = path.split('.');
152
+ let current = obj;
153
+
154
+ for (const part of parts) {
155
+ // Handle array notation like 'errors[0]'
156
+ const arrayMatch = part.match(/(\w+)\[(\d+)\]/);
157
+ if (arrayMatch) {
158
+ const [, key, index] = arrayMatch;
159
+ current = current?.[key]?.[parseInt(index, 10)];
160
+ } else {
161
+ current = current?.[part];
162
+ }
163
+
164
+ if (current === undefined || current === null) {
165
+ return null;
166
+ }
167
+ }
168
+
169
+ return current;
170
+ }
171
+
172
+ /**
173
+ * Categorize HTTP errors into standard codes
174
+ */
175
+ function categorizeHttpError(status?: number): string {
176
+ if (!status) return 'NETWORK_ERROR';
177
+
178
+ if (status === 400) return 'VALIDATION_ERROR';
179
+ if (status === 401) return 'AUTHENTICATION_ERROR';
180
+ if (status === 403) return 'AUTHORIZATION_ERROR';
181
+ if (status === 404) return 'NOT_FOUND';
182
+ if (status === 409) return 'CONFLICT';
183
+ if (status === 422) return 'VALIDATION_ERROR';
184
+ if (status === 429) return 'RATE_LIMIT';
185
+ if (status >= 500) return 'ERP_SERVER_ERROR';
186
+ if (status >= 400) return 'CLIENT_ERROR';
187
+
188
+ return 'HTTP_ERROR';
189
+ }
190
+
191
+ /**
192
+ * Format error for logging (shorter, cleaner version)
193
+ */
194
+ export function formatErrorForLogging(error: any): string {
195
+ const formatted = formatError(error);
196
+
197
+ let message = `[${formatted.code}] ${formatted.message}`;
198
+
199
+ if (formatted.httpStatus) {
200
+ message = `[${formatted.httpStatus}] ${message}`;
201
+ }
202
+
203
+ return message;
204
+ }
205
+
@@ -64,9 +64,10 @@ class AxiosClient implements HTTPClient {
64
64
  * It can be convenient to set `baseURL` for an instance of axios to pass relative URLs to methods of that instance.
65
65
  */
66
66
  constructor(baseUrl: string, retryAttempts: number) {
67
+ const timeout = parseInt(process.env.MM_API_TIMEOUT || "30000");
67
68
  this.client = axios.create({
68
69
  baseURL: baseUrl,
69
- timeout: 30000,
70
+ timeout: timeout,
70
71
  headers: {
71
72
  "Content-Type": "application/json",
72
73
  },
@@ -108,7 +109,7 @@ class AxiosClient implements HTTPClient {
108
109
  params: config.params,
109
110
  signal: controller.signal,
110
111
  };
111
- // MLW TODO
112
+
112
113
  logger.info("HTTP request starting", {
113
114
  url: config.url,
114
115
  method: config.method,
@@ -127,10 +128,8 @@ class AxiosClient implements HTTPClient {
127
128
  try {
128
129
  for (let attempt = 0; attempt <= this.retryAttempts; attempt++) {
129
130
  try {
130
- // MLW TODO
131
131
  logger.info(`HTTP request attempt ${attempt + 1}/${this.retryAttempts + 1}`);
132
132
  const response = await this.client.request<T>(axiosConfig);
133
- // MLW TODO
134
133
  logger.info("HTTP request succeeded", { status: response.status });
135
134
  return {
136
135
  data: response.data,
@@ -17,11 +17,6 @@ export {
17
17
  export { getTimezoneOffsetAndPersist } from "./time-utils.js";
18
18
  export { formatDateWithTZOffset, convertToLocalTime, toISOWithOffset } from "./timezone.js";
19
19
  export { applyTimezoneOffsetsToFields } from "./time-utils.js";
20
- export {
21
- convertUtcDateTimeToErpLocal,
22
- convertErpLocalDateTimeToUtc,
23
- getERPTimezone,
24
- } from "./erp-timezone-utils.js";
25
20
  export * from "./time-utils.js";
26
21
 
27
22
  /**
@@ -69,6 +64,12 @@ export type { HTTPClient, HTTPRequestConfig, HTTPResponse } from "./http-client.
69
64
  export * from "./mm-labor-ticket-helpers.js";
70
65
  export { getErrorType } from './error-utils.js';
71
66
 
67
+ /**
68
+ * Error formatting utilities
69
+ */
70
+ export { formatError, formatErrorForLogging } from './error-formatter.js';
71
+ export type { FormattedError } from './error-formatter.js';
72
+
72
73
  /**
73
74
  * MM Connector Logger utilities
74
75
  */
@@ -72,8 +72,6 @@ export const setInitialLoadComplete = (complete: boolean): void => {
72
72
  };
73
73
 
74
74
  /**
75
- * @deprecated The cached numeric offset cannot account for DST changes.
76
- * Prefer using the cached timezone name with Luxon helpers for conversions.
77
75
  * Gets the company's cached current timezone offset (e.g., -5)
78
76
  * @returns The cached timezone offset or 0 if not found
79
77
  */
@@ -1,13 +1,15 @@
1
+ import { convertToLocalTime, toISOWithOffset } from "./timezone.js";
1
2
  import { MMReceiveLaborTicket } from "../services/mm-api-service/types/receive-types.js";
2
- import { convertUtcDateTimeToErpLocal, getERPTimezone } from "./erp-timezone-utils.js";
3
3
 
4
4
  /**
5
- * Converts key datetime fields from UTC (MachineMetrics API) to the ERP's local timezone.
6
- * All inputs/outputs are ISO-8601 strings, and invalid inputs throw errors.
5
+ * Apply timezone offsets to datetime fields specifically for the MMApiReceiveLaborTicket object from the Zulu-based MM API
6
+ * (presumably) before sending to the ERP
7
7
  * @param laborTicket The MMApiReceiveLaborTicket object to convert
8
+ * @param timezoneOffset The timezone offset to apply
8
9
  */
9
10
  export function convertLaborTicketToLocalTimezone(
10
- laborTicket: MMReceiveLaborTicket
11
+ laborTicket: MMReceiveLaborTicket,
12
+ timezoneOffset: number
11
13
  ): MMReceiveLaborTicket {
12
14
  const timeFields = [
13
15
  "clockIn",
@@ -18,14 +20,9 @@ export function convertLaborTicketToLocalTimezone(
18
20
  "workOrderOperationClosedDate",
19
21
  ] as const;
20
22
 
21
- const timezone = getERPTimezone();
22
23
  timeFields.forEach((field) => {
23
- const value = laborTicket[field];
24
- if (value) {
25
- laborTicket[field] = convertUtcDateTimeToErpLocal(value, timezone);
26
- } else {
27
- laborTicket[field] = null;
28
- }
24
+ const localTime = convertToLocalTime(laborTicket[field], timezoneOffset);
25
+ laborTicket[field] = localTime ? toISOWithOffset(localTime, timezoneOffset) : null;
29
26
  });
30
27
  return laborTicket;
31
28
  }