facturas 0.5.2 → 0.6.1

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.
package/dist/wsmtxca.mjs CHANGED
@@ -7,158 +7,306 @@ var ArcaError = class extends Error {
7
7
  this.code = code;
8
8
  }
9
9
  };
10
+ var ArcaInputError = class extends ArcaError {
11
+ name = "ArcaInputError";
12
+ detail;
13
+ constructor(message, options) {
14
+ super(message, "ARCA_INPUT_ERROR", options);
15
+ this.detail = options?.detail;
16
+ }
17
+ };
18
+ var ArcaTransportError = class extends ArcaError {
19
+ name = "ArcaTransportError";
20
+ statusCode;
21
+ contentType;
22
+ responseBody;
23
+ constructor(message, options) {
24
+ super(message, "ARCA_TRANSPORT_ERROR", options);
25
+ this.statusCode = options?.statusCode;
26
+ this.contentType = options?.contentType;
27
+ this.responseBody = options?.responseBody;
28
+ }
29
+ };
30
+ var ArcaSoapFaultError = class extends ArcaError {
31
+ name = "ArcaSoapFaultError";
32
+ faultCode;
33
+ detail;
34
+ constructor(message, options) {
35
+ super(message, "ARCA_SOAP_FAULT", options);
36
+ this.faultCode = options?.faultCode;
37
+ this.detail = options?.detail;
38
+ }
39
+ };
40
+ var ArcaInvalidSoapResponseError = class extends ArcaError {
41
+ name = "ArcaInvalidSoapResponseError";
42
+ service;
43
+ operation;
44
+ endpointUrl;
45
+ statusCode;
46
+ contentType;
47
+ responseBodyLength;
48
+ responseBodyPreview;
49
+ parsedDetail;
50
+ constructor(message, options) {
51
+ super(message, "ARCA_INVALID_SOAP_RESPONSE", options);
52
+ this.service = options?.service;
53
+ this.operation = options?.operation;
54
+ this.endpointUrl = options?.endpointUrl;
55
+ this.statusCode = options?.statusCode;
56
+ this.contentType = options?.contentType;
57
+ this.responseBodyLength = options?.responseBodyLength;
58
+ this.responseBodyPreview = options?.responseBodyPreview;
59
+ this.parsedDetail = options?.parsedDetail;
60
+ }
61
+ };
10
62
  var ArcaServiceError = class extends ArcaError {
11
63
  name = "ArcaServiceError";
12
64
  serviceCode;
65
+ service;
66
+ operation;
67
+ result;
68
+ resultLevel;
69
+ results;
70
+ cae;
71
+ issues;
13
72
  detail;
14
73
  constructor(message, options) {
15
74
  super(message, "ARCA_SERVICE_ERROR", options);
16
75
  this.serviceCode = options?.serviceCode;
76
+ this.service = options?.service;
77
+ this.operation = options?.operation;
78
+ this.result = options?.result;
79
+ this.resultLevel = options?.resultLevel;
80
+ this.results = options?.results;
81
+ this.cae = options?.cae;
82
+ this.issues = options?.issues;
17
83
  this.detail = options?.detail;
18
84
  }
19
85
  };
20
86
 
21
87
  // src/services/wsmtxca.ts
22
88
  function createWsmtxcaService(options) {
23
- return {
24
- async authorizeVoucher({ representedTaxId, data, forceRefresh }) {
25
- const auth = await options.auth.login("wsmtxca", {
26
- representedTaxId,
27
- forceRefresh
28
- });
29
- const response = await options.soap.execute({
30
- service: "wsmtxca",
31
- operation: "autorizarComprobante",
32
- bodyElementName: "autorizarComprobanteRequest",
33
- bodyElementNamespaceMode: "prefix",
34
- body: {
35
- authRequest: createWsmtxcaAuth(
36
- representedTaxId ?? options.config.taxId,
37
- auth.token,
38
- auth.sign
39
- ),
40
- ...data
41
- }
42
- });
43
- const raw = unwrapWsmtxcaOperationResponse(
44
- response.result,
45
- "autorizarComprobante"
46
- );
47
- const authorizationPayload = extractWsmtxcaAuthorizationPayload(raw);
48
- const messages = extractWsmtxcaMessages(raw);
49
- const resultado = raw.resultado ?? authorizationPayload.resultado;
50
- const caeValue = authorizationPayload.CAE ?? authorizationPayload.codigoAutorizacion ?? raw.codigoAutorizacion;
51
- if (resultado === "R" || caeValue == null) {
52
- throw new ArcaServiceError(
53
- messages.join(" | ") || "WSMTXCA rejected the voucher authorization",
54
- { detail: raw }
55
- );
89
+ async function executeWsmtxcaAuthenticatedOperation(operation, input, body = {}, retries) {
90
+ const auth = await options.auth.login("wsmtxca", {
91
+ representedTaxId: input.representedTaxId,
92
+ forceRefresh: input.forceRefresh
93
+ });
94
+ const response = await options.soap.execute({
95
+ service: "wsmtxca",
96
+ operation,
97
+ ...retries === void 0 ? {} : { retries },
98
+ bodyElementName: `${operation}Request`,
99
+ bodyElementNamespaceMode: "prefix",
100
+ body: {
101
+ ...body,
102
+ authRequest: createWsmtxcaAuth(
103
+ input.representedTaxId ?? options.config.taxId,
104
+ auth.token,
105
+ auth.sign
106
+ )
56
107
  }
108
+ });
109
+ return unwrapWsmtxcaOperationResponse(response.result, operation);
110
+ }
111
+ async function executeWsmtxcaAuthorization({
112
+ representedTaxId,
113
+ data,
114
+ forceRefresh
115
+ }) {
116
+ if (Object.hasOwn(data, "authRequest")) {
117
+ throw new ArcaInputError(
118
+ 'WSMTXCA authorization data cannot include the reserved top-level field "authRequest".'
119
+ );
120
+ }
121
+ try {
122
+ const raw = await executeWsmtxcaAuthenticatedOperation(
123
+ "autorizarComprobante",
124
+ { representedTaxId, forceRefresh },
125
+ data,
126
+ 0
127
+ );
128
+ return { outcome: classifyWsmtxcaAuthorization(raw) };
129
+ } catch (error) {
57
130
  return {
58
- cae: String(caeValue),
59
- caeExpiry: normalizeWsmtxcaResponseDate(
60
- authorizationPayload.fechaVencimientoCAE ?? authorizationPayload.fechaVencimiento ?? raw.fechaVencimiento
61
- ),
62
- voucherNumber: parseWsmtxcaVoucherNumber(
63
- authorizationPayload.numeroComprobante ?? raw.numeroComprobante,
64
- "WSMTXCA did not return the authorized voucher number",
65
- raw
66
- ),
67
- messages,
68
- raw
131
+ outcome: createWsmtxcaIndeterminateOutcome(error),
132
+ error
69
133
  };
70
- },
71
- async getLastAuthorizedVoucher({
72
- representedTaxId,
73
- voucherType,
74
- salesPoint,
75
- forceRefresh
76
- }) {
77
- const auth = await options.auth.login("wsmtxca", {
78
- representedTaxId,
79
- forceRefresh
80
- });
81
- const response = await options.soap.execute({
82
- service: "wsmtxca",
83
- operation: "consultarUltimoComprobanteAutorizado",
84
- bodyElementName: "consultarUltimoComprobanteAutorizadoRequest",
85
- bodyElementNamespaceMode: "prefix",
86
- body: {
87
- authRequest: createWsmtxcaAuth(
88
- representedTaxId ?? options.config.taxId,
89
- auth.token,
90
- auth.sign
91
- ),
92
- consultaUltimoComprobanteAutorizadoRequest: {
93
- codigoTipoComprobante: voucherType,
94
- numeroPuntoVenta: salesPoint
95
- }
134
+ }
135
+ }
136
+ async function authorizeVoucherOutcome(input) {
137
+ return (await executeWsmtxcaAuthorization(input)).outcome;
138
+ }
139
+ async function authorizeVoucher(input) {
140
+ const execution = await executeWsmtxcaAuthorization(input);
141
+ if (execution.error) {
142
+ throw execution.error;
143
+ }
144
+ if (execution.outcome.kind !== "authorized") {
145
+ throw createWsmtxcaOutcomeError(execution.outcome);
146
+ }
147
+ const { outcome } = execution;
148
+ return {
149
+ cae: outcome.cae,
150
+ ...outcome.caeExpiry === void 0 ? {} : { caeExpiry: outcome.caeExpiry },
151
+ voucherNumber: outcome.voucherNumber,
152
+ messages: formatWsmtxcaIssues([
153
+ ...outcome.errors,
154
+ ...outcome.observations
155
+ ]),
156
+ raw: outcome.raw ?? {}
157
+ };
158
+ }
159
+ async function getLastAuthorizedVoucher({
160
+ representedTaxId,
161
+ voucherType,
162
+ salesPoint,
163
+ forceRefresh
164
+ }) {
165
+ const operation = "consultarUltimoComprobanteAutorizado";
166
+ const raw = await executeWsmtxcaAuthenticatedOperation(
167
+ operation,
168
+ { representedTaxId, forceRefresh },
169
+ {
170
+ consultaUltimoComprobanteAutorizadoRequest: {
171
+ codigoTipoComprobante: voucherType,
172
+ numeroPuntoVenta: salesPoint
96
173
  }
97
- });
98
- const raw = unwrapWsmtxcaOperationResponse(
99
- response.result,
100
- "consultarUltimoComprobanteAutorizado"
101
- );
174
+ }
175
+ );
176
+ const errors = extractWsmtxcaIssues(raw, operation, "error");
177
+ if (errors.length > 0 && errors.every((issue) => issue.code === "1502")) {
178
+ return { voucherNumber: 0, raw };
179
+ }
180
+ if (errors.length > 0) {
181
+ throw createWsmtxcaServiceError(operation, raw, errors);
182
+ }
183
+ return {
184
+ voucherNumber: parseWsmtxcaVoucherNumber(
185
+ raw.numeroComprobante ?? raw.cbteNro ?? raw.nroComprobante,
186
+ "WSMTXCA did not return the last authorized voucher number",
187
+ raw,
188
+ true
189
+ ),
190
+ raw
191
+ };
192
+ }
193
+ async function getSalesPoints({
194
+ representedTaxId,
195
+ forceRefresh
196
+ }) {
197
+ const raw = await executeWsmtxcaAuthenticatedOperation(
198
+ "consultarPuntosVenta",
199
+ { representedTaxId, forceRefresh }
200
+ );
201
+ const rawSalesPoints = toRecord(raw.arrayPuntosVenta)?.puntoVenta;
202
+ const entries = Array.isArray(rawSalesPoints) ? rawSalesPoints : rawSalesPoints ? [rawSalesPoints] : [];
203
+ const salesPoints = entries.flatMap((entry) => {
204
+ const record = toRecord(entry);
205
+ const number = parseOptionalPositiveInteger(record?.numeroPuntoVenta);
206
+ if (number === void 0) {
207
+ return [];
208
+ }
209
+ const deletedAt = normalizeWsmtxcaResponseDate(record?.fechaBaja);
210
+ return [
211
+ {
212
+ number,
213
+ blocked: String(record?.bloqueado ?? "N").toUpperCase() === "S",
214
+ ...deletedAt === void 0 ? {} : { deletedAt }
215
+ }
216
+ ];
217
+ });
218
+ return { salesPoints, raw };
219
+ }
220
+ async function lookupVoucher({
221
+ representedTaxId,
222
+ voucherType,
223
+ salesPoint,
224
+ voucherNumber,
225
+ forceRefresh
226
+ }) {
227
+ const operation = "consultarComprobante";
228
+ const raw = await executeWsmtxcaAuthenticatedOperation(
229
+ operation,
230
+ { representedTaxId, forceRefresh },
231
+ {
232
+ consultaComprobanteRequest: {
233
+ codigoTipoComprobante: voucherType,
234
+ numeroPuntoVenta: salesPoint,
235
+ numeroComprobante: voucherNumber
236
+ }
237
+ }
238
+ );
239
+ const errors = extractWsmtxcaIssues(raw, operation, "error");
240
+ const observations = extractWsmtxcaIssues(raw, operation, "observation");
241
+ if (errors.length > 0 && errors.every((issue) => issue.code === "1503")) {
102
242
  return {
103
- voucherNumber: parseWsmtxcaVoucherNumber(
104
- raw.numeroComprobante ?? raw.cbteNro ?? raw.nroComprobante,
105
- extractWsmtxcaMessages(raw).join(" | ") || "WSMTXCA did not return the last authorized voucher number",
106
- raw
107
- ),
243
+ kind: "not_found",
244
+ service: "wsmtxca",
245
+ operation,
246
+ errors,
247
+ observations,
108
248
  raw
109
249
  };
110
- },
111
- async getVoucher({
112
- representedTaxId,
113
- voucherType,
114
- salesPoint,
115
- voucherNumber,
116
- forceRefresh
117
- }) {
118
- const auth = await options.auth.login("wsmtxca", {
119
- representedTaxId,
120
- forceRefresh
121
- });
122
- const response = await options.soap.execute({
123
- service: "wsmtxca",
124
- operation: "consultarComprobante",
125
- bodyElementName: "consultarComprobanteRequest",
126
- bodyElementNamespaceMode: "prefix",
127
- body: {
128
- authRequest: createWsmtxcaAuth(
129
- representedTaxId ?? options.config.taxId,
130
- auth.token,
131
- auth.sign
132
- ),
133
- consultaComprobanteRequest: {
134
- codigoTipoComprobante: voucherType,
135
- numeroPuntoVenta: salesPoint,
136
- numeroComprobante: voucherNumber
137
- }
250
+ }
251
+ if (errors.length > 0) {
252
+ throw createWsmtxcaServiceError(operation, raw, errors);
253
+ }
254
+ const voucher = extractWsmtxcaVoucherPayload(raw);
255
+ if (voucher === raw && !toRecord(raw.comprobante)) {
256
+ throw new ArcaServiceError(
257
+ "WSMTXCA did not return the voucher issue date",
258
+ {
259
+ service: "wsmtxca",
260
+ operation,
261
+ issues: observations,
262
+ detail: raw
138
263
  }
139
- });
140
- const raw = unwrapWsmtxcaOperationResponse(
141
- response.result,
142
- "consultarComprobante"
143
264
  );
144
- const voucher = extractWsmtxcaVoucherPayload(raw);
145
- const messages = extractWsmtxcaMessages(raw);
146
- const invoiceDate = normalizeWsmtxcaResponseDate(
147
- voucher.fechaEmision ?? voucher.fecha ?? voucher.CbteFch
265
+ }
266
+ return {
267
+ kind: "found",
268
+ service: "wsmtxca",
269
+ operation,
270
+ voucher: mapWsmtxcaVoucherInfo(voucher),
271
+ observations,
272
+ raw
273
+ };
274
+ }
275
+ async function getVoucher(input) {
276
+ const lookup = await lookupVoucher(input);
277
+ if (lookup.kind === "not_found") {
278
+ throw createWsmtxcaServiceError(
279
+ lookup.operation,
280
+ lookup.raw,
281
+ lookup.errors
148
282
  );
149
- if (!invoiceDate) {
150
- throw new ArcaServiceError(
151
- messages[0] ?? "WSMTXCA did not return the voucher issue date",
152
- { detail: raw }
153
- );
154
- }
155
- return {
156
- invoiceDate,
157
- voucher,
158
- messages,
159
- raw
160
- };
161
283
  }
284
+ const invoiceDate = lookup.voucher.invoiceDate;
285
+ if (!invoiceDate) {
286
+ throw new ArcaServiceError(
287
+ formatWsmtxcaIssues(lookup.observations)[0] ?? "WSMTXCA did not return the voucher issue date",
288
+ {
289
+ service: "wsmtxca",
290
+ operation: lookup.operation,
291
+ issues: lookup.observations,
292
+ detail: lookup.raw
293
+ }
294
+ );
295
+ }
296
+ return {
297
+ invoiceDate,
298
+ voucher: lookup.voucher.raw,
299
+ messages: formatWsmtxcaIssues(lookup.observations),
300
+ raw: lookup.raw
301
+ };
302
+ }
303
+ return {
304
+ authorizeVoucherOutcome,
305
+ authorizeVoucher,
306
+ getLastAuthorizedVoucher,
307
+ getSalesPoints,
308
+ lookupVoucher,
309
+ getVoucher
162
310
  };
163
311
  }
164
312
  function createWsmtxcaAuth(representedTaxId, token, sign) {
@@ -169,7 +317,7 @@ function createWsmtxcaAuth(representedTaxId, token, sign) {
169
317
  };
170
318
  }
171
319
  function toRecord(value) {
172
- return value && typeof value === "object" ? value : void 0;
320
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
173
321
  }
174
322
  function unwrapWsmtxcaOperationResponse(response, operation) {
175
323
  const responseRecord = toRecord(response) ?? {};
@@ -179,6 +327,9 @@ function unwrapWsmtxcaOperationResponse(response, operation) {
179
327
  if (operation === "consultarComprobante") {
180
328
  return toRecord(responseRecord.consultarComprobanteResponse) ?? toRecord(responseRecord.consultaComprobanteResponse) ?? toRecord(responseRecord.consultarComprobanteResult) ?? responseRecord;
181
329
  }
330
+ if (operation === "consultarPuntosVenta") {
331
+ return toRecord(responseRecord.consultarPuntosVentaResponse) ?? toRecord(responseRecord.consultarPuntosVentaResult) ?? responseRecord;
332
+ }
182
333
  return toRecord(responseRecord.consultarUltimoComprobanteAutorizadoResponse) ?? toRecord(responseRecord.consultaUltimoComprobanteAutorizadoResponse) ?? toRecord(responseRecord.consultarUltimoComprobanteAutorizadoResult) ?? responseRecord;
183
334
  }
184
335
  function extractWsmtxcaAuthorizationPayload(raw) {
@@ -187,42 +338,291 @@ function extractWsmtxcaAuthorizationPayload(raw) {
187
338
  function extractWsmtxcaVoucherPayload(raw) {
188
339
  return toRecord(raw.comprobanteResponse) ?? toRecord(raw.comprobante) ?? toRecord(raw.cmp) ?? raw;
189
340
  }
190
- function extractWsmtxcaMessages(raw) {
191
- const rawErrors = raw.arrayErrores;
192
- const rawObservations = raw.arrayObservaciones;
193
- const toEntries = (value) => {
194
- if (!value) {
195
- return [];
196
- }
197
- if (Array.isArray(value)) {
198
- return value;
341
+ function classifyWsmtxcaAuthorization(raw) {
342
+ const operation = "autorizarComprobante";
343
+ const payload = extractWsmtxcaAuthorizationPayload(raw);
344
+ const result = normalizeWsmtxcaResult(raw.resultado ?? payload.resultado);
345
+ const cae = normalizeWsmtxcaString(
346
+ payload.CAE ?? payload.codigoAutorizacion ?? raw.codigoAutorizacion
347
+ );
348
+ const caeExpiry = normalizeWsmtxcaResponseDate(
349
+ payload.fechaVencimientoCAE ?? payload.fechaVencimiento ?? raw.fechaVencimiento
350
+ );
351
+ const voucherNumber = parseOptionalPositiveInteger(
352
+ payload.numeroComprobante ?? raw.numeroComprobante
353
+ );
354
+ const errors = extractWsmtxcaIssues(raw, operation, "error");
355
+ const observations = extractWsmtxcaIssues(raw, operation, "observation");
356
+ const base = {
357
+ service: "wsmtxca",
358
+ operation,
359
+ results: createWsmtxcaResults(result),
360
+ errors,
361
+ observations,
362
+ raw
363
+ };
364
+ if ((result === "A" || result === "O") && cae && voucherNumber !== void 0 && errors.length === 0) {
365
+ return {
366
+ ...base,
367
+ kind: "authorized",
368
+ result,
369
+ resultLevel: "operation",
370
+ cae,
371
+ ...caeExpiry === void 0 ? {} : { caeExpiry },
372
+ voucherNumber
373
+ };
374
+ }
375
+ if (result === "R" && !cae && errors.length > 0) {
376
+ return {
377
+ ...base,
378
+ kind: "rejected",
379
+ result: "R",
380
+ resultLevel: "operation"
381
+ };
382
+ }
383
+ const outcome = {
384
+ ...base,
385
+ kind: "indeterminate",
386
+ reason: result === "R" && Boolean(cae) || (result === "A" || result === "O") && Boolean(errors.length) ? "contradictory_response" : "incomplete_response",
387
+ ...result === void 0 ? {} : { result },
388
+ ...result === void 0 ? {} : { resultLevel: "operation" }
389
+ };
390
+ assignWsmtxcaValue(outcome, "cae", cae);
391
+ assignWsmtxcaValue(outcome, "caeExpiry", caeExpiry);
392
+ assignWsmtxcaValue(outcome, "voucherNumber", voucherNumber);
393
+ return outcome;
394
+ }
395
+ function createWsmtxcaIndeterminateOutcome(error) {
396
+ return {
397
+ kind: "indeterminate",
398
+ service: "wsmtxca",
399
+ operation: "autorizarComprobante",
400
+ results: {},
401
+ reason: getWsmtxcaIndeterminateReason(error),
402
+ errors: [],
403
+ observations: []
404
+ };
405
+ }
406
+ function getWsmtxcaIndeterminateReason(error) {
407
+ if (error instanceof ArcaTransportError) {
408
+ return "transport_error";
409
+ }
410
+ if (error instanceof ArcaSoapFaultError) {
411
+ return "soap_fault";
412
+ }
413
+ if (error instanceof ArcaInvalidSoapResponseError) {
414
+ return "invalid_response";
415
+ }
416
+ return "unexpected_error";
417
+ }
418
+ function createWsmtxcaOutcomeError(outcome) {
419
+ const issues = [...outcome.errors, ...outcome.observations];
420
+ const messages = formatWsmtxcaIssues(issues);
421
+ const firstIssue = issues[0];
422
+ return new ArcaServiceError(
423
+ messages.join(" | ") || (outcome.kind === "rejected" ? "WSMTXCA rejected the voucher authorization" : "WSMTXCA did not return conclusive voucher authorization data"),
424
+ {
425
+ service: "wsmtxca",
426
+ operation: outcome.operation,
427
+ ...firstIssue?.code === void 0 ? {} : { serviceCode: firstIssue.code },
428
+ ...outcome.result === void 0 ? {} : { result: outcome.result },
429
+ ...outcome.resultLevel === void 0 ? {} : { resultLevel: outcome.resultLevel },
430
+ results: outcome.results,
431
+ ...outcome.kind === "indeterminate" && outcome.cae ? { cae: outcome.cae } : {},
432
+ issues,
433
+ detail: outcome.raw
199
434
  }
200
- if (typeof value === "object") {
201
- return [value];
435
+ );
436
+ }
437
+ function createWsmtxcaResults(operationResult) {
438
+ const results = {};
439
+ assignWsmtxcaValue(results, "operation", operationResult);
440
+ return results;
441
+ }
442
+ function createWsmtxcaServiceError(operation, raw, issues) {
443
+ const firstIssue = issues[0];
444
+ return new ArcaServiceError(
445
+ formatWsmtxcaIssues(issues).join(" | ") || "WSMTXCA returned a service error",
446
+ {
447
+ service: "wsmtxca",
448
+ operation,
449
+ ...firstIssue?.code === void 0 ? {} : { serviceCode: firstIssue.code },
450
+ issues,
451
+ detail: raw
202
452
  }
203
- return [];
204
- };
205
- const errors = toEntries(rawErrors?.codigoDescripcion).map((entry) => {
206
- const code = entry.codigo == null ? "N/A" : String(entry.codigo);
207
- const description = entry.descripcion == null ? "Unknown WSMTXCA error" : String(entry.descripcion);
208
- return `Error ${code}: ${description}`;
453
+ );
454
+ }
455
+ function extractWsmtxcaIssues(raw, operation, source) {
456
+ const container = toRecord(
457
+ source === "error" ? raw.arrayErrores : raw.arrayObservaciones
458
+ );
459
+ return normalizeWsmtxcaIssueEntries(container?.codigoDescripcion).map(
460
+ (entry) => ({
461
+ service: "wsmtxca",
462
+ operation,
463
+ source,
464
+ category: source === "observation" ? "observation" : operation === "autorizarComprobante" ? "business" : "unknown",
465
+ ...entry.code === void 0 ? {} : { code: entry.code },
466
+ message: entry.message,
467
+ ...operation === "autorizarComprobante" ? { resultLevel: "operation" } : {}
468
+ })
469
+ );
470
+ }
471
+ function normalizeWsmtxcaIssueEntries(value) {
472
+ const entries = Array.isArray(value) ? value : value ? [value] : [];
473
+ return entries.map((entry) => {
474
+ const record = toRecord(entry) ?? {};
475
+ const code = record.codigo;
476
+ const description = record.descripcion;
477
+ return {
478
+ ...code === void 0 || code === null ? {} : { code: String(code) },
479
+ message: description === void 0 || description === null ? "Unknown WSMTXCA issue" : String(description)
480
+ };
209
481
  });
210
- const observations = toEntries(rawObservations?.codigoDescripcion).map(
211
- (entry) => {
212
- const code = entry.codigo == null ? "N/A" : String(entry.codigo);
213
- const description = entry.descripcion == null ? "" : String(entry.descripcion);
214
- return `Obs ${code}: ${description}`.trim();
215
- }
482
+ }
483
+ function formatWsmtxcaIssues(issues) {
484
+ return issues.map((issue) => {
485
+ const prefix = issue.source === "error" ? "Error" : "Obs";
486
+ return `${prefix}${issue.code ? ` ${issue.code}` : ""}: ${issue.message}`;
487
+ });
488
+ }
489
+ function mapWsmtxcaVoucherInfo(raw) {
490
+ const voucher = { raw };
491
+ const invoiceDate = normalizeWsmtxcaResponseDate(
492
+ raw.fechaEmision ?? raw.fecha ?? raw.CbteFch
493
+ );
494
+ const cae = normalizeWsmtxcaString(raw.codigoAutorizacion ?? raw.CAE);
495
+ const caeExpiry = normalizeWsmtxcaResponseDate(
496
+ raw.fechaVencimiento ?? raw.fechaVencimientoCAE
497
+ );
498
+ const vatAmount = sumWsmtxcaVatAmounts(raw.arraySubtotalesIVA);
499
+ assignWsmtxcaValue(
500
+ voucher,
501
+ "voucherNumber",
502
+ parseOptionalPositiveInteger(raw.numeroComprobante)
216
503
  );
217
- return [...errors, ...observations];
504
+ assignWsmtxcaValue(voucher, "invoiceDate", invoiceDate);
505
+ assignWsmtxcaValue(
506
+ voucher,
507
+ "salesPoint",
508
+ parseOptionalPositiveInteger(raw.numeroPuntoVenta)
509
+ );
510
+ assignWsmtxcaValue(
511
+ voucher,
512
+ "voucherType",
513
+ parseOptionalPositiveInteger(raw.codigoTipoComprobante)
514
+ );
515
+ assignWsmtxcaValue(
516
+ voucher,
517
+ "concept",
518
+ parseOptionalNumber(raw.codigoConcepto)
519
+ );
520
+ assignWsmtxcaValue(
521
+ voucher,
522
+ "documentType",
523
+ parseOptionalNumber(raw.codigoTipoDocumento)
524
+ );
525
+ assignWsmtxcaValue(
526
+ voucher,
527
+ "documentNumber",
528
+ normalizeWsmtxcaString(raw.numeroDocumento)
529
+ );
530
+ assignWsmtxcaValue(
531
+ voucher,
532
+ "receiverVatConditionId",
533
+ parseOptionalNumber(raw.condicionIVAReceptor)
534
+ );
535
+ assignWsmtxcaValue(
536
+ voucher,
537
+ "totalAmount",
538
+ parseOptionalNumber(raw.importeTotal)
539
+ );
540
+ assignWsmtxcaValue(
541
+ voucher,
542
+ "subtotalAmount",
543
+ parseOptionalNumber(raw.importeSubtotal)
544
+ );
545
+ assignWsmtxcaValue(
546
+ voucher,
547
+ "taxableAmount",
548
+ parseOptionalNumber(raw.importeGravado)
549
+ );
550
+ assignWsmtxcaValue(
551
+ voucher,
552
+ "nonTaxableAmount",
553
+ parseOptionalNumber(raw.importeNoGravado)
554
+ );
555
+ assignWsmtxcaValue(
556
+ voucher,
557
+ "exemptAmount",
558
+ parseOptionalNumber(raw.importeExento)
559
+ );
560
+ assignWsmtxcaValue(
561
+ voucher,
562
+ "taxAmount",
563
+ parseOptionalNumber(raw.importeOtrosTributos)
564
+ );
565
+ assignWsmtxcaValue(voucher, "vatAmount", vatAmount);
566
+ assignWsmtxcaValue(
567
+ voucher,
568
+ "currencyId",
569
+ normalizeWsmtxcaString(raw.codigoMoneda)
570
+ );
571
+ assignWsmtxcaValue(
572
+ voucher,
573
+ "exchangeRate",
574
+ parseOptionalNumber(raw.cotizacionMoneda)
575
+ );
576
+ assignWsmtxcaValue(voucher, "cae", cae);
577
+ assignWsmtxcaValue(voucher, "caeExpiry", caeExpiry);
578
+ return voucher;
579
+ }
580
+ function sumWsmtxcaVatAmounts(value) {
581
+ const subtotals = toRecord(value)?.subtotalIVA;
582
+ const entries = Array.isArray(subtotals) ? subtotals : subtotals ? [subtotals] : [];
583
+ const amounts = entries.map((entry) => parseOptionalNumber(toRecord(entry)?.importe)).filter((amount) => amount !== void 0);
584
+ return amounts.length > 0 ? amounts.reduce((total, amount) => total + amount, 0) : void 0;
218
585
  }
219
- function parseWsmtxcaVoucherNumber(value, message, detail) {
586
+ function parseWsmtxcaVoucherNumber(value, message, detail, allowZero = false) {
220
587
  const parsed = Number.parseInt(String(value ?? ""), 10);
221
- if (!Number.isFinite(parsed) || parsed <= 0) {
222
- throw new ArcaServiceError(message, { detail });
588
+ if (!Number.isFinite(parsed) || (allowZero ? parsed < 0 : parsed <= 0)) {
589
+ throw new ArcaServiceError(message, {
590
+ service: "wsmtxca",
591
+ detail
592
+ });
223
593
  }
224
594
  return parsed;
225
595
  }
596
+ function parseOptionalPositiveInteger(value) {
597
+ const parsed = Number.parseInt(String(value ?? ""), 10);
598
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
599
+ }
600
+ function parseOptionalNumber(value) {
601
+ if (value === void 0 || value === null || value === "") {
602
+ return void 0;
603
+ }
604
+ const parsed = Number(value);
605
+ return Number.isFinite(parsed) ? parsed : void 0;
606
+ }
607
+ function normalizeWsmtxcaResult(value) {
608
+ if (typeof value !== "string") {
609
+ return void 0;
610
+ }
611
+ const normalized = value.trim().toUpperCase();
612
+ return normalized || void 0;
613
+ }
614
+ function normalizeWsmtxcaString(value) {
615
+ if (value === void 0 || value === null) {
616
+ return void 0;
617
+ }
618
+ const normalized = String(value).trim();
619
+ return normalized || void 0;
620
+ }
621
+ function assignWsmtxcaValue(target, key, value) {
622
+ if (value !== void 0) {
623
+ target[key] = value;
624
+ }
625
+ }
226
626
  function normalizeWsmtxcaResponseDate(value) {
227
627
  if (typeof value === "number" && Number.isInteger(value)) {
228
628
  return formatCompactDateToIso(value);