@ramiidv/arca-mtxca 0.2.0 → 0.4.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.
@@ -1,374 +1,349 @@
1
- import { afipSoapCall, ensureArray } from '@ramiidv/arca-common';
2
- import { MTXCA_ENDPOINTS, MTXCA_NAMESPACE } from './constants.js';
3
- function extractObservaciones(result) {
4
- const obs = result['arrayObservaciones'];
5
- if (!obs)
6
- return [];
7
- const arr = ensureArray(obs['codigoDescripcion']);
8
- return arr.map((o) => ({
9
- code: Number(o['codigo'] ?? 0),
10
- msg: String(o['descripcion'] ?? ''),
11
- }));
1
+ import { ArcaSoapError, checkServiceErrors, parseServerStatus, SoapServiceClient } from '@ramiidv/arca-common';
2
+ import { decodeResponse, encodeRequest } from '@ramiidv/arca-common/schema';
3
+ import { MTXCA_NAMESPACE } from './constants.js';
4
+ import { ACTIVIDADES_VIGENTES_RESPONSE, CAEA_ENTRE_FECHAS_RESPONSE, CAEA_NO_UTILIZADO, CAEA_NO_UTILIZADO_PTO_VTA, CAEA_RESPONSE, COMPROBANTE_CAE_RESPONSE, COMPROBANTE_CAEA_RESPONSE, CON_CAEA, CON_CAEA_PTO_VTA, CONDICIONES_IVA_RECEPTOR_RESPONSE, CONSULTA_COMPROBANTE, CONSULTA_COMPROBANTE_RESPONSE, CONSULTA_CONDICIONES_IVA, CONSULTA_ULTIMO, COTIZACION, COTIZACION_RESPONSE, ENTRE_FECHAS, FECHA_PROCESO, PARAMETROS, PUNTOS_VENTA_RESPONSE, RESULTADO, SOLICITUD_CAEA, ULTIMO_RESPONSE, } from './schemas.js';
5
+ import { buildComprobante, encodeConCaea } from './serialize.js';
6
+ const SERVICE = 'WSMTXCA';
7
+ function isResultado(value) {
8
+ return value === 'A' || value === 'O' || value === 'R';
12
9
  }
13
- function extractErrores(result) {
14
- const errs = result['arrayErrores'];
15
- if (!errs)
16
- return [];
17
- const arr = ensureArray(errs['codigoDescripcion']);
18
- return arr.map((e) => ({
19
- code: Number(e['codigo'] ?? 0),
20
- msg: String(e['descripcion'] ?? ''),
21
- }));
10
+ /**
11
+ * Respuesta ilegible: `ArcaSoapError` (`uncertain` en emisiones: el
12
+ * comprobante pudo haberse procesado aunque la respuesta no se pueda leer).
13
+ */
14
+ function invalid(message, kind) {
15
+ return new ArcaSoapError(message, { uncertain: kind === 'emision' });
16
+ }
17
+ function decode(node, schema, path, kind) {
18
+ return decodeResponse(node, schema, path, { error: (m) => invalid(m, kind) });
19
+ }
20
+ /**
21
+ * Elemento obligatorio del XSD que el DSL no verifica: un contenedor de lista
22
+ * (`[]` si viene vacío) o un elemento complejo (`complex`: tiene que tener hijos).
23
+ */
24
+ function requerido(node, key, path, kind, complex = false) {
25
+ const v = node[key];
26
+ const ok = complex ? v !== null && typeof v === 'object' && !Array.isArray(v) : v !== undefined && v !== null;
27
+ if (!ok)
28
+ throw invalid(`Respuesta inválida: falta ${path}.${key}`, kind);
22
29
  }
23
- function extractItems(result) {
24
- const items = result['arrayItems'];
25
- if (!items)
26
- return [];
27
- const arr = ensureArray(items['item']);
28
- return arr.map((i) => ({
29
- codigo: i['codigo'] != null ? String(i['codigo']) : undefined,
30
- descripcion: String(i['descripcion'] ?? ''),
31
- cantidad: Number(i['cantidad'] ?? 0),
32
- unidadMedida: Number(i['unidadMedida'] ?? 0),
33
- precioUnitario: Number(i['precioUnitario'] ?? 0),
34
- importeItem: Number(i['importeItem'] ?? 0),
35
- codigoCondicionIVA: Number(i['codigoCondicionIVA'] ?? 0),
36
- importeIVA: Number(i['importeIVA'] ?? 0),
37
- importeBonificacion: i['importeBonificacion'] != null
38
- ? Number(i['importeBonificacion'])
39
- : undefined,
40
- }));
30
+ function toCAEA({ CAEA, arrayObservaciones, ...rest }) {
31
+ return { ...rest, caea: CAEA, observaciones: arrayObservaciones };
41
32
  }
42
33
  /**
43
- * Low-level client for the WSMTXCA web service.
44
- * Wraps each SOAP method. Does not handle authentication.
34
+ * Cliente de bajo nivel de WSMTXCA: una función por operación del WSDL. No
35
+ * maneja la autenticación (recibe `MtxcaAuth`) ni la numeración.
36
+ *
37
+ * - Valida los requests contra el XSD y redondea importes (`ArcaValidationError`).
38
+ * - Las consultas lanzan `ArcaServiceError` si ARCA devuelve `arrayErrores`.
39
+ * - Las emisiones (`autorizar*`, `informar*`, `solicitarCAEA`) usan
40
+ * `kind: 'emision'`: no se reintentan y, si no hay respuesta, el
41
+ * `ArcaSoapError` tiene `uncertain: true`.
45
42
  */
46
- export class MtxcaClient {
47
- endpoint;
48
- soapOptions;
49
- onEvent;
43
+ export class MtxcaClient extends SoapServiceClient {
50
44
  constructor(config) {
51
- this.endpoint = config.production
52
- ? MTXCA_ENDPOINTS.production
53
- : MTXCA_ENDPOINTS.testing;
54
- this.soapOptions = config.soapOptions;
55
- this.onEvent = config.onEvent;
45
+ if (!config?.endpoint)
46
+ throw new TypeError('MtxcaClient: endpoint es obligatorio');
47
+ const { endpoint, timeout, retries, retryDelayMs, onEvent, fetch, legacyTls } = config;
48
+ super({
49
+ endpoint,
50
+ namespace: MTXCA_NAMESPACE,
51
+ transport: { timeout, retries, retryDelayMs, onEvent, fetch, legacyTls },
52
+ });
53
+ }
54
+ call(method, params, kind, opts, requestElement = `${method}Request`) {
55
+ return this.soapCall(method, params, {
56
+ kind,
57
+ signal: opts?.signal,
58
+ requestElement,
59
+ responseElement: `${method}Response`,
60
+ soapActionFromMethod: true,
61
+ // Todo como texto: los schemas convierten cada campo según el XSD.
62
+ parseNumbers: false,
63
+ });
64
+ }
65
+ static authRequest(auth) {
66
+ return { token: auth.token, sign: auth.sign, cuitRepresentada: auth.cuitRepresentada };
56
67
  }
57
- call(method, params) {
58
- return afipSoapCall(this.endpoint, MTXCA_NAMESPACE, method, params, { ...this.soapOptions, onEvent: this.onEvent });
68
+ /** Llamada autenticada: `authRequest` primero, como en todos los request del XSD. */
69
+ callAuth(method, auth, params, kind, opts) {
70
+ return this.call(method, { authRequest: MtxcaClient.authRequest(auth), ...params }, kind, opts);
59
71
  }
72
+ /** Consulta: lanza `ArcaServiceError` si la respuesta trae `arrayErrores`. */
73
+ async consulta(method, auth, params, opts) {
74
+ const result = await this.callAuth(method, auth, params, 'consulta', opts);
75
+ checkServiceErrors(result, `${SERVICE} ${method}`);
76
+ return result;
77
+ }
78
+ // =========================================================================
79
+ // Estado
60
80
  // =========================================================================
61
- // Facturacion
81
+ /** `dummy`: estado de los servidores. No requiere autenticación. */
82
+ async dummy(opts) {
83
+ return parseServerStatus(await this.call('dummy', {}, 'consulta', opts, null));
84
+ }
85
+ // =========================================================================
86
+ // CAE
62
87
  // =========================================================================
63
88
  /**
64
- * Autoriza un comprobante con detalle de articulos.
89
+ * `autorizarComprobante`. Valida contra el XSD y redondea; no valida la
90
+ * coherencia de importes (eso lo hace `ArcaMtxca.autorizar`). Un rechazo
91
+ * devuelve `resultado: 'R'` con `errores` (no lanza).
65
92
  */
66
- async autorizarComprobante(auth, request) {
67
- const itemsArray = request.invoice.items.map((item) => {
68
- const mapped = {
69
- descripcion: item.descripcion,
70
- cantidad: item.cantidad,
71
- unidadMedida: item.unidadMedida,
72
- precioUnitario: item.precioUnitario,
73
- importeItem: item.importeItem,
74
- codigoCondicionIVA: item.codigoCondicionIVA,
75
- importeIVA: item.importeIVA,
76
- };
77
- if (item.codigo != null)
78
- mapped['codigo'] = item.codigo;
79
- if (item.importeBonificacion != null)
80
- mapped['importeBonificacion'] = item.importeBonificacion;
81
- return mapped;
82
- });
83
- const comprobante = {
84
- codigoTipoComprobante: request.cbteTipo,
85
- numeroPuntoVenta: request.ptoVta,
86
- codigoConcepto: request.invoice.concepto,
87
- codigoTipoDocumento: request.invoice.docTipo,
88
- numeroDocumento: request.invoice.docNro,
89
- fechaEmision: request.invoice.cbteFch,
90
- importeTotal: request.invoice.importeTotal,
91
- arrayItems: {
92
- item: itemsArray,
93
- },
94
- };
95
- // Optional fields
96
- if (request.invoice.codigoMoneda != null) {
97
- comprobante['codigoMoneda'] = request.invoice.codigoMoneda;
98
- }
99
- if (request.invoice.cotizacionMoneda != null) {
100
- comprobante['cotizacionMoneda'] = request.invoice.cotizacionMoneda;
101
- }
102
- if (request.invoice.importeGravado != null) {
103
- comprobante['importeGravado'] = request.invoice.importeGravado;
104
- }
105
- if (request.invoice.importeNoGravado != null) {
106
- comprobante['importeNoGravado'] = request.invoice.importeNoGravado;
107
- }
108
- if (request.invoice.importeExento != null) {
109
- comprobante['importeExento'] = request.invoice.importeExento;
110
- }
111
- if (request.invoice.importeOtrosTributos != null) {
112
- comprobante['importeOtrosTributos'] = request.invoice.importeOtrosTributos;
113
- }
114
- // Subtotales de IVA
115
- if (request.invoice.subtotalesIVA && request.invoice.subtotalesIVA.length > 0) {
116
- comprobante['arraySubtotalesIVA'] = {
117
- subtotalIVA: request.invoice.subtotalesIVA.map((s) => ({
118
- codigo: s.codigo,
119
- baseImponible: s.baseImponible,
120
- importe: s.importe,
121
- })),
122
- };
123
- }
124
- // Otros tributos
125
- if (request.invoice.otrosTributos && request.invoice.otrosTributos.length > 0) {
126
- comprobante['arrayOtrosTributos'] = {
127
- otroTributo: request.invoice.otrosTributos.map((t) => {
128
- const mapped = {
129
- codigo: t.codigoTributo,
130
- baseImponible: t.baseImponible,
131
- importe: t.importe,
132
- };
133
- if (t.descripcion != null)
134
- mapped['descripcion'] = t.descripcion;
135
- if (t.alicuota != null)
136
- mapped['alicuota'] = t.alicuota;
137
- return mapped;
138
- }),
139
- };
140
- }
141
- // Comprobantes asociados
142
- if (request.invoice.comprobanteAsociado && request.invoice.comprobanteAsociado.length > 0) {
143
- comprobante['arrayComprobantesAsociados'] = {
144
- comprobanteAsociado: request.invoice.comprobanteAsociado.map((c) => {
145
- const mapped = {
146
- codigoTipoComprobante: c.codigoTipoComprobante,
147
- numeroPuntoVenta: c.numeroPuntoVenta,
148
- numeroComprobante: c.numeroComprobante,
149
- };
150
- if (c.cuit != null)
151
- mapped['cuit'] = c.cuit;
152
- return mapped;
153
- }),
154
- };
155
- }
156
- // Fechas de servicio
157
- if (request.invoice.fechaDesde != null) {
158
- comprobante['fechaDesde'] = request.invoice.fechaDesde;
159
- }
160
- if (request.invoice.fechaHasta != null) {
161
- comprobante['fechaHasta'] = request.invoice.fechaHasta;
162
- }
163
- if (request.invoice.fechaVtoPago != null) {
164
- comprobante['fechaVtoPago'] = request.invoice.fechaVtoPago;
93
+ async autorizarComprobante(auth, comprobante, opts) {
94
+ return this.autorizarCAE('autorizarComprobante', auth, comprobante, opts);
95
+ }
96
+ /** `autorizarAjusteIVA`: comprobante de ajuste de IVA con CAE (ND/NC 2, 3, 7, 8, 52, 53). */
97
+ async autorizarAjusteIVA(auth, comprobante, opts) {
98
+ return this.autorizarCAE('autorizarAjusteIVA', auth, comprobante, opts);
99
+ }
100
+ async autorizarCAE(method, auth, comprobante, opts) {
101
+ const comprobanteCAERequest = buildComprobante(comprobante, 'cae');
102
+ const result = await this.callAuth(method, auth, { comprobanteCAERequest }, 'emision', opts);
103
+ const { resultado, base } = this.resultado(result, method);
104
+ if (resultado === 'R')
105
+ return { resultado, ...base };
106
+ requerido(result, 'comprobanteResponse', method, 'emision', true);
107
+ const { CAE, ...cr } = decode(result['comprobanteResponse'], COMPROBANTE_CAE_RESPONSE, `${method}.comprobanteResponse`, 'emision');
108
+ return { resultado, comprobante: { ...cr, cae: CAE }, ...base };
109
+ }
110
+ /**
111
+ * Decodifica `resultado`, `arrayObservaciones`, `arrayErrores` y `evento`.
112
+ * `resultado` es obligatorio: si falta y hay `arrayErrores`, es un rechazo
113
+ * (`ArcaServiceError`); si falta sin errores, la respuesta es ilegible
114
+ * (`ArcaSoapError` con `uncertain`).
115
+ */
116
+ resultado(result, method, schema = RESULTADO) {
117
+ const r = decode(result, schema, method, 'emision');
118
+ if (!r.resultado) {
119
+ checkServiceErrors(result, `${SERVICE} ${method}`);
120
+ throw invalid(`Respuesta inválida: falta ${method}.resultado`, 'emision');
165
121
  }
166
- // Observaciones
167
- if (request.invoice.observaciones != null) {
168
- comprobante['observaciones'] = request.invoice.observaciones;
122
+ if (!isResultado(r.resultado)) {
123
+ throw invalid(`Respuesta inválida: valor "${r.resultado}" inesperado en ${method}.resultado`, 'emision');
169
124
  }
170
- const result = await this.call('autorizarComprobante', {
171
- authRequest: {
172
- token: auth.Token,
173
- sign: auth.Sign,
174
- cuitRepresentada: auth.Cuit,
175
- },
176
- comprobanteCAERequest: comprobante,
177
- });
178
- const comprobanteResponse = (result['comprobanteResponse'] ?? result);
179
125
  return {
180
- resultado: String(comprobanteResponse['resultado'] ?? ''),
181
- cae: comprobanteResponse['CAE'] != null
182
- ? String(comprobanteResponse['CAE'])
183
- : undefined,
184
- caeFchVto: comprobanteResponse['fechaVencimientoCAE'] != null
185
- ? String(comprobanteResponse['fechaVencimientoCAE'])
186
- : undefined,
187
- cbteNro: Number(comprobanteResponse['numeroComprobante'] ?? 0),
188
- observaciones: extractObservaciones(result),
189
- errores: extractErrores(result),
126
+ resultado: r.resultado,
127
+ base: { observaciones: r.arrayObservaciones, errores: r.arrayErrores, evento: r.evento },
190
128
  };
191
129
  }
130
+ // =========================================================================
131
+ // CAEA
132
+ // =========================================================================
192
133
  /**
193
- * Consulta un comprobante previamente autorizado.
134
+ * `solicitarCAEA`. `periodo` AAAAMM, `orden` 1 (primera quincena) o 2.
135
+ * Lanza `ArcaServiceError` si ARCA lo rechaza. Es una emisión: si el
136
+ * resultado es incierto, recuperalo con `consultarCAEAEntreFechas`.
194
137
  */
195
- async consultarComprobante(auth, cbteTipo, ptoVta, cbteNro) {
196
- const result = await this.call('consultarComprobante', {
197
- authRequest: {
198
- token: auth.Token,
199
- sign: auth.Sign,
200
- cuitRepresentada: auth.Cuit,
201
- },
202
- consultaComprobanteRequest: {
203
- codigoTipoComprobante: cbteTipo,
204
- numeroPuntoVenta: ptoVta,
205
- numeroComprobante: cbteNro,
206
- },
138
+ async solicitarCAEA(auth, periodo, orden, opts) {
139
+ const solicitudCAEA = encodeRequest({ periodo, orden }, SOLICITUD_CAEA, 'solicitarCAEA', (i, errors) => {
140
+ const p = i['periodo'];
141
+ if (Number.isInteger(p) && (p % 100 < 1 || p % 100 > 12)) {
142
+ errors.push({ field: 'periodo', message: 'periodo: debe tener formato AAAAMM (mes 01 a 12)', value: p });
143
+ }
207
144
  });
208
- const comp = (result['comprobante'] ?? result);
145
+ const result = await this.callAuth('solicitarCAEA', auth, { solicitudCAEA }, 'emision', opts);
146
+ checkServiceErrors(result, `${SERVICE} solicitarCAEA`);
147
+ return this.parseCAEA(result, 'solicitarCAEA', 'emision');
148
+ }
149
+ /** `consultarCAEA`: datos de un CAEA otorgado. */
150
+ async consultarCAEA(auth, caea, opts) {
151
+ const params = encodeConCaea({ caea }, CON_CAEA, 'consultarCAEA');
152
+ const result = await this.consulta('consultarCAEA', auth, params, opts);
153
+ return this.parseCAEA(result, 'consultarCAEA', 'consulta');
154
+ }
155
+ /** `CAEAResponse` obligatorio de `solicitarCAEA` / `consultarCAEA`. */
156
+ parseCAEA(result, method, kind) {
157
+ requerido(result, 'CAEAResponse', method, kind, true);
158
+ return toCAEA(decode(result['CAEAResponse'], CAEA_RESPONSE, `${method}.CAEAResponse`, kind));
159
+ }
160
+ /** `consultarCAEAEntreFechas`: CAEAs otorgados con vigencia entre dos fechas. */
161
+ async consultarCAEAEntreFechas(auth, fechaDesde, fechaHasta, opts) {
162
+ const params = encodeRequest({ fechaDesde, fechaHasta }, ENTRE_FECHAS, 'consultarCAEAEntreFechas');
163
+ const result = await this.consulta('consultarCAEAEntreFechas', auth, params, opts);
164
+ const { arrayCAEAResponse } = decode(result, CAEA_ENTRE_FECHAS_RESPONSE, 'consultarCAEAEntreFechas', 'consulta');
165
+ return arrayCAEAResponse.map(toCAEA);
166
+ }
167
+ /** `informarComprobanteCAEA`. Un rechazo devuelve `resultado: 'R'` con `errores`. */
168
+ async informarComprobanteCAEA(auth, comprobante, opts) {
169
+ return this.informarCAEA('informarComprobanteCAEA', auth, comprobante, opts);
170
+ }
171
+ /** `informarAjusteIVACAEA`. Un rechazo devuelve `resultado: 'R'` con `errores`. */
172
+ async informarAjusteIVACAEA(auth, comprobante, opts) {
173
+ return this.informarCAEA('informarAjusteIVACAEA', auth, comprobante, opts);
174
+ }
175
+ async informarCAEA(method, auth, comprobante, opts) {
176
+ const comprobanteCAEARequest = buildComprobante(comprobante, 'caea');
177
+ const result = await this.callAuth(method, auth, { comprobanteCAEARequest }, 'emision', opts);
178
+ const { resultado, base } = this.resultado(result, method);
179
+ const { fechaProceso } = decode(result, FECHA_PROCESO, method, 'emision');
180
+ if (resultado === 'R')
181
+ return { resultado, fechaProceso, ...base };
182
+ if (fechaProceso === undefined)
183
+ throw invalid(`Respuesta inválida: falta ${method}.fechaProceso`, 'emision');
184
+ requerido(result, 'comprobanteCAEAResponse', method, 'emision', true);
185
+ const { CAEA, ...cr } = decode(result['comprobanteCAEAResponse'], COMPROBANTE_CAEA_RESPONSE, `${method}.comprobanteCAEAResponse`, 'emision');
186
+ return { resultado, fechaProceso, comprobante: { caea: CAEA, ...cr }, ...base };
187
+ }
188
+ /** `informarCAEANoUtilizado`: informa que un CAEA no se usó en ningún punto de venta. */
189
+ async informarCAEANoUtilizado(auth, caea, opts) {
190
+ const method = 'informarCAEANoUtilizado';
191
+ return this.caeaNoUtilizado(method, auth, encodeConCaea({ caea }, CON_CAEA, method), CAEA_NO_UTILIZADO, opts);
192
+ }
193
+ /** `informarCAEANoUtilizadoPtoVta`: informa que un CAEA no se usó en un punto de venta. */
194
+ async informarCAEANoUtilizadoPtoVta(auth, caea, numeroPuntoVenta, opts) {
195
+ const method = 'informarCAEANoUtilizadoPtoVta';
196
+ const params = encodeConCaea({ caea, numeroPuntoVenta }, CON_CAEA_PTO_VTA, method);
197
+ return this.caeaNoUtilizado(method, auth, params, CAEA_NO_UTILIZADO_PTO_VTA, opts);
198
+ }
199
+ async caeaNoUtilizado(method, auth, params, schema, opts) {
200
+ const result = await this.callAuth(method, auth, params, 'emision', opts);
201
+ const { resultado, base } = this.resultado(result, method, schema);
202
+ const r = decode(result, schema, method, 'emision');
203
+ const conPtoVta = 'numeroPuntoVenta' in params;
204
+ // Obligatorios en el XSD; en un rechazo se toleran ausentes.
205
+ const faltante = [
206
+ r.fechaProceso === undefined && 'fechaProceso',
207
+ r.CAEA === undefined && 'CAEA',
208
+ conPtoVta && r.numeroPuntoVenta === undefined && 'numeroPuntoVenta',
209
+ ].find(Boolean);
210
+ if (resultado !== 'R' && faltante)
211
+ throw invalid(`Respuesta inválida: falta ${method}.${faltante}`, 'emision');
209
212
  return {
210
- codigoTipoComprobante: Number(comp['codigoTipoComprobante'] ?? 0),
211
- numeroPuntoVenta: Number(comp['numeroPuntoVenta'] ?? 0),
212
- numeroComprobante: Number(comp['numeroComprobante'] ?? 0),
213
- fechaEmision: String(comp['fechaEmision'] ?? ''),
214
- codigoTipoDocumento: Number(comp['codigoTipoDocumento'] ?? 0),
215
- numeroDocumento: Number(comp['numeroDocumento'] ?? 0),
216
- importeTotal: Number(comp['importeTotal'] ?? 0),
217
- importeNoGravado: Number(comp['importeNoGravado'] ?? 0),
218
- importeGravado: Number(comp['importeGravado'] ?? 0),
219
- importeExento: Number(comp['importeExento'] ?? 0),
220
- importeOtrosTributos: Number(comp['importeOtrosTributos'] ?? 0),
221
- importeSubtotal: Number(comp['importeSubtotal'] ?? 0),
222
- codigoMoneda: String(comp['codigoMoneda'] ?? 'PES'),
223
- cotizacionMoneda: Number(comp['cotizacionMoneda'] ?? 1),
224
- resultado: String(comp['resultado'] ?? ''),
225
- codigoAutorizacion: comp['codigoAutorizacion'] != null
226
- ? String(comp['codigoAutorizacion'])
227
- : undefined,
228
- fechaVencimiento: comp['fechaVencimiento'] != null
229
- ? String(comp['fechaVencimiento'])
230
- : undefined,
231
- observaciones: extractObservaciones(comp),
232
- items: extractItems(comp),
213
+ resultado,
214
+ fechaProceso: r.fechaProceso,
215
+ caea: r.CAEA,
216
+ ...(conPtoVta ? { numeroPuntoVenta: r.numeroPuntoVenta } : {}),
217
+ errores: base.errores,
218
+ evento: base.evento,
233
219
  };
234
220
  }
221
+ /** `consultarPtosVtaCAEANoInformados`: puntos de venta que todavía no informaron movimientos para el CAEA. */
222
+ async consultarPtosVtaCAEANoInformados(auth, caea, opts) {
223
+ const method = 'consultarPtosVtaCAEANoInformados';
224
+ const result = await this.consulta(method, auth, encodeConCaea({ caea }, CON_CAEA, method), opts);
225
+ return this.parsePuntosVenta(result, method);
226
+ }
227
+ // =========================================================================
228
+ // Consultas de comprobantes
229
+ // =========================================================================
235
230
  /**
236
- * Consulta el ultimo comprobante autorizado.
231
+ * `consultarUltimoComprobanteAutorizado`. Lanza `ArcaServiceError` ante
232
+ * cualquier error, incluido 1502 (no hay comprobantes para ese punto de venta
233
+ * y tipo); `ArcaMtxca.ultimoComprobante` lo traduce a 0.
237
234
  */
238
- async consultarUltimoComprobanteAutorizado(auth, ptoVta, cbteTipo) {
239
- const result = await this.call('consultarUltimoComprobanteAutorizado', {
240
- authRequest: {
241
- token: auth.Token,
242
- sign: auth.Sign,
243
- cuitRepresentada: auth.Cuit,
244
- },
245
- consultaUltimoComprobanteAutorizadoRequest: {
246
- codigoTipoComprobante: cbteTipo,
247
- numeroPuntoVenta: ptoVta,
235
+ async consultarUltimoComprobanteAutorizado(auth, ptoVta, cbteTipo, opts) {
236
+ const method = 'consultarUltimoComprobanteAutorizado';
237
+ const consultaUltimoComprobanteAutorizadoRequest = encodeRequest({ codigoTipoComprobante: cbteTipo, numeroPuntoVenta: ptoVta }, CONSULTA_ULTIMO, method);
238
+ const result = await this.consulta(method, auth, { consultaUltimoComprobanteAutorizadoRequest }, opts);
239
+ return decode(result, ULTIMO_RESPONSE, method, 'consulta').numeroComprobante;
240
+ }
241
+ /** `consultarComprobante`: datos de un comprobante autorizado (CAE) o informado (CAEA). */
242
+ async consultarComprobante(auth, cbteTipo, ptoVta, cbteNro, opts) {
243
+ const method = 'consultarComprobante';
244
+ const consultaComprobanteRequest = encodeRequest({ codigoTipoComprobante: cbteTipo, numeroPuntoVenta: ptoVta, numeroComprobante: cbteNro }, CONSULTA_COMPROBANTE, method);
245
+ const result = await this.consulta(method, auth, { consultaComprobanteRequest }, opts);
246
+ requerido(result, 'comprobante', method, 'consulta', true);
247
+ requerido(result['comprobante'], 'arrayItems', `${method}.comprobante`, 'consulta');
248
+ const r = decode(result, CONSULTA_COMPROBANTE_RESPONSE, method, 'consulta');
249
+ const { arrayComprobantesAsociados, arrayOtrosTributos, arrayItems, arraySubtotalesIVA, arrayDatosAdicionales, arrayCompradores, arrayActividades, ...c } = r.comprobante;
250
+ return {
251
+ comprobante: {
252
+ ...c,
253
+ comprobantesAsociados: arrayComprobantesAsociados,
254
+ otrosTributos: arrayOtrosTributos,
255
+ items: arrayItems,
256
+ subtotalesIVA: arraySubtotalesIVA,
257
+ datosAdicionales: arrayDatosAdicionales,
258
+ compradores: arrayCompradores,
259
+ actividades: arrayActividades.map((a) => a.codigo),
248
260
  },
249
- });
250
- return Number(result['numeroComprobante'] ?? 0);
261
+ observaciones: r.arrayObservaciones,
262
+ evento: r.evento,
263
+ };
251
264
  }
252
265
  // =========================================================================
253
- // Parametros
266
+ // Parámetros
254
267
  // =========================================================================
268
+ /** Consulta cuya respuesta es un `ArrayCodigosDescripciones[String]Type` obligatorio (ver `PARAMETROS`). */
269
+ async codigos(method, auth, opts) {
270
+ const schema = PARAMETROS[method];
271
+ const [container] = Object.keys(schema);
272
+ const result = await this.consulta(method, auth, {}, opts);
273
+ requerido(result, container, method, 'consulta');
274
+ return decode(result, schema, method, 'consulta')[container];
275
+ }
276
+ consultarTiposComprobante(auth, opts) {
277
+ return this.codigos('consultarTiposComprobante', auth, opts);
278
+ }
279
+ consultarTiposDocumento(auth, opts) {
280
+ return this.codigos('consultarTiposDocumento', auth, opts);
281
+ }
282
+ consultarAlicuotasIVA(auth, opts) {
283
+ return this.codigos('consultarAlicuotasIVA', auth, opts);
284
+ }
285
+ consultarCondicionesIVA(auth, opts) {
286
+ return this.codigos('consultarCondicionesIVA', auth, opts);
287
+ }
288
+ /** Condiciones de IVA del receptor admitidas para un tipo de comprobante (RG 5616). */
289
+ async consultarCondicionesIVAReceptor(auth, cbteTipo, opts) {
290
+ const method = 'consultarCondicionesIVAReceptor';
291
+ const consultaCondicionesIVAReceptorRequest = encodeRequest({ codigoTipoComprobante: cbteTipo }, CONSULTA_CONDICIONES_IVA, method);
292
+ const result = await this.consulta(method, auth, { consultaCondicionesIVAReceptorRequest }, opts);
293
+ return decode(result, CONDICIONES_IVA_RECEPTOR_RESPONSE, method, 'consulta').arrayCondicionesIVAReceptor;
294
+ }
295
+ consultarUnidadesMedida(auth, opts) {
296
+ return this.codigos('consultarUnidadesMedida', auth, opts);
297
+ }
298
+ consultarTiposTributo(auth, opts) {
299
+ return this.codigos('consultarTiposTributo', auth, opts);
300
+ }
301
+ consultarTiposDatosAdicionales(auth, opts) {
302
+ return this.codigos('consultarTiposDatosAdicionales', auth, opts);
303
+ }
304
+ consultarMonedas(auth, opts) {
305
+ return this.codigos('consultarMonedas', auth, opts);
306
+ }
255
307
  /**
256
- * Health check. No requiere autenticacion.
308
+ * `consultarCotizacionMoneda`. `cotizacion` es `null` si ARCA no tiene
309
+ * cotización para la moneda en esa fecha (responde sin valor y sin error).
257
310
  */
258
- async dummy() {
259
- const result = await this.call('dummy', {});
311
+ async consultarCotizacionMoneda(auth, codigoMoneda, fechaCotizacion, opts) {
312
+ const method = 'consultarCotizacionMoneda';
313
+ const params = encodeRequest({ codigoMoneda, fechaCotizacion }, COTIZACION, method);
314
+ const result = await this.consulta(method, auth, params, opts);
315
+ const { cotizacionMoneda } = decode(result, COTIZACION_RESPONSE, method, 'consulta');
260
316
  return {
261
- appserver: String(result['appserver'] ?? ''),
262
- dbserver: String(result['dbserver'] ?? ''),
263
- authserver: String(result['authserver'] ?? ''),
317
+ codigoMoneda: params['codigoMoneda'],
318
+ fechaCotizacion: params['fechaCotizacion'],
319
+ cotizacion: cotizacionMoneda ?? null,
264
320
  };
265
321
  }
266
- async consultarTiposComprobante(auth) {
267
- return this.getParam(auth, 'consultarTiposComprobante', 'arrayTiposComprobante', 'codigoDescripcion');
322
+ /** `ArrayPuntosVentaType` (obligatorio salvo en `consultarPtosVtaCAEANoInformados`). */
323
+ parsePuntosVenta(result, method, obligatorio = false) {
324
+ if (obligatorio)
325
+ requerido(result, 'arrayPuntosVenta', method, 'consulta');
326
+ return decode(result, PUNTOS_VENTA_RESPONSE, method, 'consulta')
327
+ .arrayPuntosVenta;
268
328
  }
269
- async consultarTiposDocumento(auth) {
270
- return this.getParam(auth, 'consultarTiposDocumento', 'arrayTiposDocumento', 'codigoDescripcion');
329
+ async puntosVenta(method, auth, opts) {
330
+ return this.parsePuntosVenta(await this.consulta(method, auth, {}, opts), method, true);
271
331
  }
272
- async consultarAlicuotasIVA(auth) {
273
- return this.getParam(auth, 'consultarAlicuotasIVA', 'arrayAlicuotasIVA', 'codigoDescripcion');
332
+ consultarPuntosVenta(auth, opts) {
333
+ return this.puntosVenta('consultarPuntosVenta', auth, opts);
274
334
  }
275
- async consultarCondicionesIVA(auth) {
276
- return this.getParam(auth, 'consultarCondicionesIVA', 'arrayCondicionesIVA', 'codigoDescripcion');
335
+ consultarPuntosVentaCAE(auth, opts) {
336
+ return this.puntosVenta('consultarPuntosVentaCAE', auth, opts);
277
337
  }
278
- async consultarMonedas(auth) {
279
- const result = await this.call('consultarMonedas', {
280
- authRequest: {
281
- token: auth.Token,
282
- sign: auth.Sign,
283
- cuitRepresentada: auth.Cuit,
284
- },
285
- });
286
- const container = (result['arrayMonedas'] ?? result);
287
- const arr = ensureArray(container['codigoDescripcion']);
288
- return arr.map((m) => ({
289
- Id: String(m['codigo'] ?? ''),
290
- Desc: String(m['descripcion'] ?? ''),
291
- FchDesde: m['fechaDesde'] != null ? String(m['fechaDesde']) : undefined,
292
- FchHasta: m['fechaHasta'] != null ? String(m['fechaHasta']) : undefined,
293
- }));
294
- }
295
- async consultarUnidadesMedida(auth) {
296
- const result = await this.call('consultarUnidadesMedida', {
297
- authRequest: {
298
- token: auth.Token,
299
- sign: auth.Sign,
300
- cuitRepresentada: auth.Cuit,
301
- },
302
- });
303
- const container = (result['arrayUnidadesMedida'] ?? result);
304
- const arr = ensureArray(container['codigoDescripcion']);
305
- return arr.map((u) => ({
306
- Id: Number(u['codigo'] ?? 0),
307
- Desc: String(u['descripcion'] ?? ''),
308
- FchDesde: u['fechaDesde'] != null ? String(u['fechaDesde']) : undefined,
309
- FchHasta: u['fechaHasta'] != null ? String(u['fechaHasta']) : undefined,
310
- }));
311
- }
312
- async consultarPuntosVenta(auth) {
313
- return this.getPtosVenta(auth, 'consultarPuntosVenta', 'arrayPuntosVenta');
314
- }
315
- async consultarPuntosVentaCAE(auth) {
316
- return this.getPtosVenta(auth, 'consultarPuntosVentaCAE', 'arrayPuntosVenta');
317
- }
318
- async consultarPuntosVentaCAEA(auth) {
319
- return this.getPtosVenta(auth, 'consultarPuntosVentaCAEA', 'arrayPuntosVenta');
320
- }
321
- async consultarCotizacionMoneda(auth, monedaId) {
322
- const result = await this.call('consultarCotizacionMoneda', {
323
- authRequest: {
324
- token: auth.Token,
325
- sign: auth.Sign,
326
- cuitRepresentada: auth.Cuit,
327
- },
328
- codigoMoneda: monedaId,
329
- });
330
- return {
331
- monedaId: String(result['codigoMoneda'] ?? monedaId),
332
- cotizacion: Number(result['cotizacionMoneda'] ?? 0),
333
- fechaCotizacion: String(result['fechaCotizacion'] ?? ''),
334
- };
338
+ consultarPuntosVentaCAEA(auth, opts) {
339
+ return this.puntosVenta('consultarPuntosVentaCAEA', auth, opts);
335
340
  }
336
- // =========================================================================
337
- // Private helpers
338
- // =========================================================================
339
- async getParam(auth, method, containerKey, itemKey) {
340
- const result = await this.call(method, {
341
- authRequest: {
342
- token: auth.Token,
343
- sign: auth.Sign,
344
- cuitRepresentada: auth.Cuit,
345
- },
346
- });
347
- const container = (result[containerKey] ?? result);
348
- const arr = ensureArray(container[itemKey]);
349
- return arr.map((item) => ({
350
- Id: Number(item['codigo'] ?? 0),
351
- Desc: String(item['descripcion'] ?? ''),
352
- FchDesde: item['fechaDesde'] != null ? String(item['fechaDesde']) : undefined,
353
- FchHasta: item['fechaHasta'] != null ? String(item['fechaHasta']) : undefined,
354
- }));
355
- }
356
- async getPtosVenta(auth, method, containerKey) {
357
- const result = await this.call(method, {
358
- authRequest: {
359
- token: auth.Token,
360
- sign: auth.Sign,
361
- cuitRepresentada: auth.Cuit,
362
- },
363
- });
364
- const container = (result[containerKey] ?? result);
365
- const arr = ensureArray(container['puntoVenta']);
366
- return arr.map((pv) => ({
367
- Nro: Number(pv['numeroPuntoVenta'] ?? 0),
368
- EmisionTipo: String(pv['tipoEmision'] ?? ''),
369
- Bloqueado: String(pv['bloqueado'] ?? ''),
370
- FchBaja: String(pv['fechaBaja'] ?? ''),
371
- }));
341
+ /** `consultarActividadesVigentes`: actividades vigentes del emisor. */
342
+ async consultarActividadesVigentes(auth, opts) {
343
+ const method = 'consultarActividadesVigentes';
344
+ const result = await this.consulta(method, auth, {}, opts);
345
+ return decode(result, ACTIVIDADES_VIGENTES_RESPONSE, method, 'consulta')
346
+ .arrayActividades;
372
347
  }
373
348
  }
374
349
  //# sourceMappingURL=mtxca-client.js.map