facturas 0.5.2 → 0.6.0

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