facturas 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.
package/dist/wsfe.js ADDED
@@ -0,0 +1,605 @@
1
+ // src/errors.ts
2
+ var ArcaError = class extends Error {
3
+ code;
4
+ name = "ArcaError";
5
+ constructor(message, code = "ARCA_ERROR", options) {
6
+ super(message, options);
7
+ this.code = code;
8
+ }
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 ArcaServiceError = class extends ArcaError {
19
+ name = "ArcaServiceError";
20
+ serviceCode;
21
+ detail;
22
+ constructor(message, options) {
23
+ super(message, "ARCA_SERVICE_ERROR", options);
24
+ this.serviceCode = options?.serviceCode;
25
+ this.detail = options?.detail;
26
+ }
27
+ };
28
+
29
+ // src/services/wsfe.ts
30
+ function createWsfeService(options) {
31
+ async function executeWsfeAuthenticatedOperation(operation, input, body = {}) {
32
+ const auth = await options.auth.login("wsfe", {
33
+ representedTaxId: input.representedTaxId,
34
+ forceRefresh: input.forceAuthRefresh
35
+ });
36
+ const response = await options.soap.execute({
37
+ service: "wsfe",
38
+ operation,
39
+ body: {
40
+ Auth: createWsfeAuth(
41
+ input.representedTaxId ?? options.config.taxId,
42
+ auth.token,
43
+ auth.sign
44
+ ),
45
+ ...body
46
+ }
47
+ });
48
+ return unwrapWsfeOperationResult(operation, response.result);
49
+ }
50
+ async function executeWsfeOperation(operation, body = {}) {
51
+ const response = await options.soap.execute({
52
+ service: "wsfe",
53
+ operation,
54
+ body
55
+ });
56
+ return unwrapWsfeOperationResult(operation, response.result);
57
+ }
58
+ async function getNextVoucherNumber({
59
+ representedTaxId,
60
+ salesPoint,
61
+ voucherType,
62
+ forceAuthRefresh
63
+ }) {
64
+ const result = await executeWsfeAuthenticatedOperation(
65
+ "FECompUltimoAutorizado",
66
+ {
67
+ representedTaxId,
68
+ forceAuthRefresh
69
+ },
70
+ {
71
+ PtoVta: salesPoint,
72
+ CbteTipo: voucherType
73
+ }
74
+ );
75
+ return Number(result.CbteNro ?? 0) + 1;
76
+ }
77
+ async function getWsfeCatalog(operation, resultKey, input) {
78
+ const result = await executeWsfeAuthenticatedOperation(operation, {
79
+ representedTaxId: input.representedTaxId,
80
+ forceAuthRefresh: input.forceAuthRefresh
81
+ });
82
+ return getWsfeResultEntries(result, resultKey).map(mapWsfeCatalogEntry);
83
+ }
84
+ return {
85
+ async createNextVoucher({ representedTaxId, data }) {
86
+ const normalizedInput = normalizeWsfeVoucherInput(data);
87
+ const voucherNumber = await getNextVoucherNumber({
88
+ representedTaxId,
89
+ salesPoint: normalizedInput.salesPoint,
90
+ voucherType: normalizedInput.voucherType
91
+ });
92
+ const requestData = mapWsfeVoucherInput(normalizedInput, voucherNumber);
93
+ const auth = await options.auth.login("wsfe", { representedTaxId });
94
+ const response = await options.soap.execute({
95
+ service: "wsfe",
96
+ operation: "FECAESolicitar",
97
+ body: {
98
+ Auth: createWsfeAuth(
99
+ representedTaxId ?? options.config.taxId,
100
+ auth.token,
101
+ auth.sign
102
+ ),
103
+ FeCAEReq: {
104
+ FeCabReq: {
105
+ CantReg: 1,
106
+ PtoVta: normalizedInput.salesPoint,
107
+ CbteTipo: normalizedInput.voucherType
108
+ },
109
+ FeDetReq: {
110
+ FECAEDetRequest: requestData
111
+ }
112
+ }
113
+ }
114
+ });
115
+ const result = unwrapWsfeOperationResult(
116
+ "FECAESolicitar",
117
+ response.result
118
+ );
119
+ const detailResponse = normalizeWsfeDetailResponse(result);
120
+ const cae = detailResponse.CAE;
121
+ const caeExpiry = detailResponse.CAEFchVto;
122
+ if (typeof cae !== "string" || typeof caeExpiry !== "string") {
123
+ throw new ArcaServiceError(
124
+ "WSFE did not return CAE authorization data",
125
+ { detail: result }
126
+ );
127
+ }
128
+ return {
129
+ cae,
130
+ caeExpiry: String(caeExpiry),
131
+ voucherNumber,
132
+ raw: result
133
+ };
134
+ },
135
+ getNextVoucherNumber,
136
+ getLastVoucher(input) {
137
+ return getNextVoucherNumber(input);
138
+ },
139
+ async getSalesPoints({ representedTaxId, forceAuthRefresh }) {
140
+ const result = await executeWsfeAuthenticatedOperation(
141
+ "FEParamGetPtosVenta",
142
+ {
143
+ representedTaxId,
144
+ forceAuthRefresh
145
+ }
146
+ );
147
+ const rawPoints = result.ResultGet?.PtoVenta;
148
+ if (!rawPoints) {
149
+ return [];
150
+ }
151
+ const entries = Array.isArray(rawPoints) ? rawPoints : [rawPoints];
152
+ return entries.map(mapWsfeSalesPoint);
153
+ },
154
+ getVoucherTypes(input) {
155
+ return getWsfeCatalog("FEParamGetTiposCbte", "CbteTipo", input);
156
+ },
157
+ getDocumentTypes(input) {
158
+ return getWsfeCatalog("FEParamGetTiposDoc", "DocTipo", input);
159
+ },
160
+ getConceptTypes(input) {
161
+ return getWsfeCatalog("FEParamGetTiposConcepto", "ConceptoTipo", input);
162
+ },
163
+ async getCurrencyTypes({ representedTaxId, forceAuthRefresh }) {
164
+ const result = await executeWsfeAuthenticatedOperation(
165
+ "FEParamGetTiposMonedas",
166
+ {
167
+ representedTaxId,
168
+ forceAuthRefresh
169
+ }
170
+ );
171
+ return getWsfeResultEntries(result, "Moneda").map(mapWsfeCurrencyType);
172
+ },
173
+ getVatRates(input) {
174
+ return getWsfeCatalog("FEParamGetTiposIva", "IvaTipo", input);
175
+ },
176
+ getTaxTypes(input) {
177
+ return getWsfeCatalog("FEParamGetTiposTributos", "TributoTipo", input);
178
+ },
179
+ getOptionalTypes(input) {
180
+ return getWsfeCatalog("FEParamGetTiposOpcional", "OpcionalTipo", input);
181
+ },
182
+ async getActivities({ representedTaxId, forceAuthRefresh }) {
183
+ const result = await executeWsfeAuthenticatedOperation(
184
+ "FEParamGetActividades",
185
+ {
186
+ representedTaxId,
187
+ forceAuthRefresh
188
+ }
189
+ );
190
+ return getWsfeResultEntries(result, "ActividadesTipo").map(
191
+ mapWsfeActivityType
192
+ );
193
+ },
194
+ async getReceiverVatConditions({
195
+ representedTaxId,
196
+ voucherClass,
197
+ forceAuthRefresh
198
+ }) {
199
+ const result = await executeWsfeAuthenticatedOperation(
200
+ "FEParamGetCondicionIvaReceptor",
201
+ {
202
+ representedTaxId,
203
+ forceAuthRefresh
204
+ },
205
+ {
206
+ ...voucherClass === void 0 ? {} : { ClaseCmp: voucherClass }
207
+ }
208
+ );
209
+ return getWsfeResultEntries(result, "CondicionIvaReceptor").map(
210
+ mapWsfeReceiverVatCondition
211
+ );
212
+ },
213
+ async getServerStatus() {
214
+ const result = await executeWsfeOperation("FEDummy");
215
+ return mapWsfeServerStatus(result);
216
+ },
217
+ async getQuotation({ currencyId, representedTaxId, forceAuthRefresh }) {
218
+ const result = await executeWsfeAuthenticatedOperation(
219
+ "FEParamGetCotizacion",
220
+ {
221
+ representedTaxId,
222
+ forceAuthRefresh
223
+ },
224
+ {
225
+ MonId: currencyId
226
+ }
227
+ );
228
+ const raw = result.ResultGet ?? {};
229
+ return mapWsfeQuotation(raw);
230
+ },
231
+ async getVoucherInfo({
232
+ representedTaxId,
233
+ number,
234
+ salesPoint,
235
+ voucherType
236
+ }) {
237
+ const result = await executeWsfeAuthenticatedOperation(
238
+ "FECompConsultar",
239
+ {
240
+ representedTaxId
241
+ },
242
+ {
243
+ FeCompConsReq: {
244
+ CbteNro: number,
245
+ PtoVta: salesPoint,
246
+ CbteTipo: voucherType
247
+ }
248
+ }
249
+ );
250
+ const raw = result.ResultGet ?? null;
251
+ if (!raw) {
252
+ return null;
253
+ }
254
+ return mapWsfeVoucherInfo(raw);
255
+ }
256
+ };
257
+ }
258
+ function mapWsfeVoucherInput(input, voucherNumber) {
259
+ const data = {
260
+ Concepto: input.concept,
261
+ DocTipo: input.documentType,
262
+ DocNro: input.documentNumber,
263
+ CbteDesde: voucherNumber,
264
+ CbteHasta: voucherNumber,
265
+ CbteFch: input.voucherDate,
266
+ ImpTotal: input.totalAmount,
267
+ ImpTotConc: input.nonTaxableAmount,
268
+ ImpNeto: input.netAmount,
269
+ ImpOpEx: input.exemptAmount,
270
+ ImpTrib: input.taxAmount,
271
+ ImpIVA: input.vatAmount,
272
+ MonId: input.currencyId,
273
+ MonCotiz: input.exchangeRate,
274
+ PtoVta: input.salesPoint,
275
+ CbteTipo: input.voucherType
276
+ };
277
+ if (input.receiverVatConditionId !== void 0) {
278
+ data.CondicionIVAReceptorId = input.receiverVatConditionId;
279
+ }
280
+ if (input.sameCurrencyForeignCancellation !== void 0) {
281
+ data.CanMisMonExt = input.sameCurrencyForeignCancellation;
282
+ }
283
+ if (input.serviceStartDate !== void 0) {
284
+ data.FchServDesde = input.serviceStartDate;
285
+ }
286
+ if (input.serviceEndDate !== void 0) {
287
+ data.FchServHasta = input.serviceEndDate;
288
+ }
289
+ if (input.paymentDueDate !== void 0) {
290
+ data.FchVtoPago = input.paymentDueDate;
291
+ }
292
+ if (input.associatedVouchers) {
293
+ data.CbtesAsoc = {
294
+ CbteAsoc: input.associatedVouchers.map((v) => ({
295
+ Tipo: v.type,
296
+ PtoVta: v.salesPoint,
297
+ Nro: v.number,
298
+ ...v.taxId === void 0 ? {} : { Cuit: v.taxId },
299
+ ...v.voucherDate === void 0 ? {} : { CbteFch: v.voucherDate }
300
+ }))
301
+ };
302
+ }
303
+ if (input.associatedPeriod) {
304
+ data.PeriodoAsoc = {
305
+ FchDesde: input.associatedPeriod.startDate,
306
+ FchHasta: input.associatedPeriod.endDate
307
+ };
308
+ }
309
+ if (input.taxes) {
310
+ data.Tributos = {
311
+ Tributo: input.taxes.map((t) => ({
312
+ Id: t.id,
313
+ ...t.description === void 0 ? {} : { Desc: t.description },
314
+ BaseImp: t.baseAmount,
315
+ Alic: t.rate,
316
+ Importe: t.amount
317
+ }))
318
+ };
319
+ }
320
+ if (input.vatRates) {
321
+ data.Iva = {
322
+ AlicIva: input.vatRates.map((v) => ({
323
+ Id: v.id,
324
+ BaseImp: v.baseAmount,
325
+ Importe: v.amount
326
+ }))
327
+ };
328
+ }
329
+ if (input.optionalFields) {
330
+ data.Opcionales = {
331
+ Opcional: input.optionalFields.map((o) => ({
332
+ Id: o.id,
333
+ Valor: o.value
334
+ }))
335
+ };
336
+ }
337
+ if (input.buyers) {
338
+ data.Compradores = {
339
+ Comprador: input.buyers.map((b) => ({
340
+ DocTipo: b.documentType,
341
+ DocNro: b.documentNumber,
342
+ Porcentaje: b.percentage
343
+ }))
344
+ };
345
+ }
346
+ if (input.activities) {
347
+ data.Actividades = {
348
+ Actividad: input.activities.map((a) => ({
349
+ Id: a.id
350
+ }))
351
+ };
352
+ }
353
+ return data;
354
+ }
355
+ function normalizeWsfeVoucherInput(input) {
356
+ const {
357
+ voucherDate,
358
+ serviceStartDate,
359
+ serviceEndDate,
360
+ paymentDueDate,
361
+ associatedVouchers,
362
+ associatedPeriod,
363
+ ...rest
364
+ } = input;
365
+ return {
366
+ ...rest,
367
+ voucherDate: normalizeWsfeDateInput(voucherDate, "voucherDate"),
368
+ ...serviceStartDate === void 0 ? {} : {
369
+ serviceStartDate: normalizeWsfeDateInput(
370
+ serviceStartDate,
371
+ "serviceStartDate"
372
+ )
373
+ },
374
+ ...serviceEndDate === void 0 ? {} : {
375
+ serviceEndDate: normalizeWsfeDateInput(
376
+ serviceEndDate,
377
+ "serviceEndDate"
378
+ )
379
+ },
380
+ ...paymentDueDate === void 0 ? {} : {
381
+ paymentDueDate: normalizeWsfeDateInput(
382
+ paymentDueDate,
383
+ "paymentDueDate"
384
+ )
385
+ },
386
+ ...associatedVouchers === void 0 ? {} : {
387
+ associatedVouchers: associatedVouchers.map((voucher, index) => {
388
+ const { voucherDate: associatedVoucherDate, ...associatedRest } = voucher;
389
+ return {
390
+ ...associatedRest,
391
+ ...associatedVoucherDate === void 0 ? {} : {
392
+ voucherDate: normalizeWsfeDateInput(
393
+ associatedVoucherDate,
394
+ `associatedVouchers[${index}].voucherDate`
395
+ )
396
+ }
397
+ };
398
+ })
399
+ },
400
+ ...associatedPeriod === void 0 ? {} : {
401
+ associatedPeriod: {
402
+ startDate: normalizeWsfeDateInput(
403
+ associatedPeriod.startDate,
404
+ "associatedPeriod.startDate"
405
+ ),
406
+ endDate: normalizeWsfeDateInput(
407
+ associatedPeriod.endDate,
408
+ "associatedPeriod.endDate"
409
+ )
410
+ }
411
+ }
412
+ };
413
+ }
414
+ function normalizeWsfeDateInput(value, fieldName) {
415
+ if (typeof value !== "string") {
416
+ throw new ArcaInputError(
417
+ `Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
418
+ {
419
+ detail: { field: fieldName, value }
420
+ }
421
+ );
422
+ }
423
+ const normalizedValue = value.trim();
424
+ const afipMatch = normalizedValue.match(/^(\d{4})(\d{2})(\d{2})$/);
425
+ if (afipMatch) {
426
+ const [, year, month, day] = afipMatch;
427
+ assertValidCalendarDate(year, month, day, fieldName, normalizedValue);
428
+ return normalizedValue;
429
+ }
430
+ const isoMatch = normalizedValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
431
+ if (isoMatch) {
432
+ const [, year, month, day] = isoMatch;
433
+ assertValidCalendarDate(year, month, day, fieldName, normalizedValue);
434
+ return `${year}${month}${day}`;
435
+ }
436
+ throw new ArcaInputError(
437
+ `Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
438
+ {
439
+ detail: { field: fieldName, value: normalizedValue }
440
+ }
441
+ );
442
+ }
443
+ function assertValidCalendarDate(yearInput, monthInput, dayInput, fieldName, value) {
444
+ const year = Number(yearInput);
445
+ const month = Number(monthInput);
446
+ const day = Number(dayInput);
447
+ const candidate = new Date(Date.UTC(year, month - 1, day));
448
+ if (candidate.getUTCFullYear() !== year || candidate.getUTCMonth() !== month - 1 || candidate.getUTCDate() !== day) {
449
+ throw new ArcaInputError(
450
+ `Invalid WSFE ${fieldName}: received a non-existent calendar date`,
451
+ {
452
+ detail: { field: fieldName, value }
453
+ }
454
+ );
455
+ }
456
+ }
457
+ function mapWsfeSalesPoint(raw) {
458
+ const record = raw;
459
+ return {
460
+ number: Number(record.Nro ?? 0),
461
+ ...record.EmisionTipo === void 0 ? {} : { emissionType: String(record.EmisionTipo) },
462
+ ...record.Bloqueado === void 0 ? {} : { blocked: String(record.Bloqueado) },
463
+ ...record.FchBaja === void 0 ? {} : { deletedSince: String(record.FchBaja) }
464
+ };
465
+ }
466
+ function mapWsfeCatalogEntry(raw) {
467
+ const record = raw;
468
+ return {
469
+ id: Number(record.Id ?? 0),
470
+ description: String(record.Desc ?? "")
471
+ };
472
+ }
473
+ function mapWsfeActivityType(raw) {
474
+ const record = raw;
475
+ return {
476
+ id: Number(record.Id ?? 0),
477
+ description: String(record.Desc ?? ""),
478
+ order: Number(record.Orden ?? 0)
479
+ };
480
+ }
481
+ function mapWsfeReceiverVatCondition(raw) {
482
+ const record = raw;
483
+ return {
484
+ id: Number(record.Id ?? 0),
485
+ description: String(record.Desc ?? ""),
486
+ voucherClass: String(record.Cmp_Clase ?? "")
487
+ };
488
+ }
489
+ function mapWsfeCurrencyType(raw) {
490
+ const record = raw;
491
+ return {
492
+ id: String(record.Id ?? ""),
493
+ description: String(record.Desc ?? ""),
494
+ validFrom: String(record.FchDesde ?? ""),
495
+ validTo: String(record.FchHasta ?? "")
496
+ };
497
+ }
498
+ function mapWsfeServerStatus(raw) {
499
+ return {
500
+ appServer: String(raw.AppServer ?? ""),
501
+ dbServer: String(raw.DbServer ?? ""),
502
+ authServer: String(raw.AuthServer ?? "")
503
+ };
504
+ }
505
+ function mapWsfeQuotation(raw) {
506
+ return {
507
+ currencyId: String(raw.MonId ?? ""),
508
+ rate: Number(raw.MonCotiz ?? 0),
509
+ date: String(raw.FchCotiz ?? "")
510
+ };
511
+ }
512
+ function mapWsfeVoucherInfo(raw) {
513
+ return {
514
+ voucherNumber: Number(raw.CbteDesde ?? raw.CbteHasta ?? 0),
515
+ ...raw.CbteFch === void 0 ? {} : { voucherDate: String(raw.CbteFch) },
516
+ ...raw.PtoVta === void 0 ? {} : { salesPoint: Number(raw.PtoVta) },
517
+ ...raw.CbteTipo === void 0 ? {} : { voucherType: Number(raw.CbteTipo) },
518
+ ...raw.ImpTotal === void 0 ? {} : { totalAmount: Number(raw.ImpTotal) },
519
+ ...raw.Resultado === void 0 ? {} : { result: String(raw.Resultado) },
520
+ ...raw.CAE === void 0 ? {} : { cae: String(raw.CAE) },
521
+ ...raw.CAEFchVto === void 0 ? {} : { caeExpiry: String(raw.CAEFchVto) },
522
+ raw
523
+ };
524
+ }
525
+ function createWsfeAuth(representedTaxId, token, sign) {
526
+ return {
527
+ Token: token,
528
+ Sign: sign,
529
+ Cuit: Number.parseInt(String(representedTaxId), 10)
530
+ };
531
+ }
532
+ function unwrapWsfeOperationResult(operation, response) {
533
+ const operationResponse = response[`${operation}Response`];
534
+ const result = operationResponse?.[`${operation}Result`] ?? response[`${operation}Result`] ?? response;
535
+ if (operation === "FECAESolicitar") {
536
+ const detailResponse = normalizeWsfeDetailResponse(result);
537
+ const resultCode = detailResponse.Resultado;
538
+ if (resultCode && resultCode !== "A") {
539
+ const observationsContainer = detailResponse.Observaciones;
540
+ const observations = normalizeWsfeErrors(observationsContainer?.Obs);
541
+ if (observations.length > 0) {
542
+ const firstObservation = observations[0];
543
+ if (!firstObservation) {
544
+ throw new ArcaServiceError(
545
+ "WSFE returned an empty observation list",
546
+ {
547
+ detail: result
548
+ }
549
+ );
550
+ }
551
+ throw new ArcaServiceError(firstObservation.message, {
552
+ serviceCode: firstObservation.code,
553
+ detail: result
554
+ });
555
+ }
556
+ }
557
+ }
558
+ const errorsContainer = result.Errors;
559
+ const errors = normalizeWsfeErrors(errorsContainer?.Err);
560
+ if (errors.length > 0) {
561
+ const firstError = errors[0];
562
+ if (!firstError) {
563
+ throw new ArcaServiceError("WSFE returned an empty error list", {
564
+ detail: result
565
+ });
566
+ }
567
+ throw new ArcaServiceError(firstError.message, {
568
+ serviceCode: firstError.code,
569
+ detail: result
570
+ });
571
+ }
572
+ return result;
573
+ }
574
+ function normalizeWsfeDetailResponse(result) {
575
+ const detailResponse = result.FeDetResp;
576
+ const rawDetail = detailResponse?.FECAEDetResponse;
577
+ if (Array.isArray(rawDetail)) {
578
+ return rawDetail[0] ?? {};
579
+ }
580
+ return rawDetail ?? {};
581
+ }
582
+ function normalizeWsfeErrors(rawErrors) {
583
+ const entries = Array.isArray(rawErrors) ? rawErrors : rawErrors ? [rawErrors] : [];
584
+ return entries.map((entry) => entry).map((entry) => {
585
+ const code = entry.Code ?? entry.code ?? "N/A";
586
+ const message = entry.Msg ?? entry.msg ?? "Unknown WSFE error";
587
+ return {
588
+ code: String(code),
589
+ message: `(${String(code)}) ${String(message)}`
590
+ };
591
+ });
592
+ }
593
+ function getWsfeResultEntries(result, key) {
594
+ const rawEntries = result.ResultGet?.[key];
595
+ if (!rawEntries) {
596
+ return [];
597
+ }
598
+ return (Array.isArray(rawEntries) ? rawEntries : [rawEntries]).map(
599
+ (entry) => entry
600
+ );
601
+ }
602
+ export {
603
+ createWsfeService
604
+ };
605
+ //# sourceMappingURL=wsfe.js.map