@acontplus/core 1.0.5 → 1.0.6

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/index.cjs.js ADDED
@@ -0,0 +1,742 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@acontplus/utils');
4
+ var uuid = require('uuid');
5
+
6
+ class AxiosAdapter {
7
+ constructor() {
8
+ //this.axios = axios.create({ baseURL: HttpClientFactory.getBaseURL() });
9
+ }
10
+ async get(url, options) {
11
+ // const res = await this.axios.get<T>(url, { headers: options?.headers, params: options?.params });
12
+ // return res.data;
13
+ throw new Error('override');
14
+ }
15
+ delete(url, options) {
16
+ throw new Error('override');
17
+ }
18
+ post(url, body, options) {
19
+ throw new Error('override');
20
+ }
21
+ put(url, body, options) {
22
+ throw new Error('override');
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Clase que actúa como fábrica y contenedor para un cliente HTTP configurable.
28
+ * Es agnóstica al framework, por lo que puede usarse en React, Vue, Angular, etc.
29
+ */
30
+ class HttpClientFactory {
31
+ /**
32
+ * Configura el cliente HTTP y opcionalmente la URL base.
33
+ * Este método debe llamarse al iniciar la aplicación.
34
+ *
35
+ * @param client - Instancia que implementa HttpPort (por ejemplo, AxiosAdapter)
36
+ * @param baseURL - URL base para las peticiones (opcional)
37
+ */
38
+ static configure(client, baseURL) {
39
+ this.client = client;
40
+ if (baseURL) this.baseURL = baseURL;
41
+ }
42
+ /**
43
+ * Devuelve el cliente HTTP configurado.
44
+ * Si no se ha configurado explícitamente, se usa el cliente por defecto.
45
+ *
46
+ * @returns Instancia de HttpPort
47
+ */
48
+ static getClient() {
49
+ if (!this.client) {
50
+ throw new Error('HttpClientFactory: No se configuró ningún cliente HTTP');
51
+ }
52
+ return this.client;
53
+ }
54
+ /**
55
+ * Devuelve la URL base configurada.
56
+ * Si no se ha definido, retorna una cadena vacía.
57
+ *
58
+ * @returns string con la baseURL
59
+ */
60
+ static getBaseURL() {
61
+ return this.baseURL;
62
+ }
63
+ }
64
+
65
+ class FetchAdapter {
66
+ async get(url, options) {
67
+ return this.request('GET', url, undefined, options);
68
+ }
69
+ async post(url, body, options) {
70
+ return this.request('POST', url, body, options);
71
+ }
72
+ async put(url, body, options) {
73
+ return this.request('PUT', url, body, options);
74
+ }
75
+ async delete(url, options) {
76
+ return this.request('DELETE', url, undefined, options);
77
+ }
78
+ async request(method, url, body, options) {
79
+ const baseURL = HttpClientFactory.getBaseURL();
80
+ const response = await fetch(`${baseURL}${url}`, {
81
+ method,
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ ...(options?.headers || {})
85
+ },
86
+ body: body ? JSON.stringify(body) : undefined
87
+ });
88
+ if (!response.ok) {
89
+ throw new Error(`HTTP error ${response.status}`);
90
+ }
91
+ return response.json();
92
+ }
93
+ }
94
+
95
+ exports.SRI_DOCUMENT_TYPE = void 0;
96
+ (function (SRI_DOCUMENT_TYPE) {
97
+ SRI_DOCUMENT_TYPE["RUC"] = "04";
98
+ SRI_DOCUMENT_TYPE["CEDULA"] = "05";
99
+ SRI_DOCUMENT_TYPE["PASSPORT"] = "06";
100
+ SRI_DOCUMENT_TYPE["CONSUMIDOR_FINAL"] = "07";
101
+ SRI_DOCUMENT_TYPE["IDENTIFICACION_EXTERIOR"] = "08";
102
+ })(exports.SRI_DOCUMENT_TYPE || (exports.SRI_DOCUMENT_TYPE = {}));
103
+ exports.SRI_DOCUMENT_TYPE_CUSTOM = void 0;
104
+ (function (SRI_DOCUMENT_TYPE_CUSTOM) {
105
+ SRI_DOCUMENT_TYPE_CUSTOM["RUC"] = "R";
106
+ SRI_DOCUMENT_TYPE_CUSTOM["CEDULA"] = "C";
107
+ SRI_DOCUMENT_TYPE_CUSTOM["PASSPORT"] = "P";
108
+ SRI_DOCUMENT_TYPE_CUSTOM["CONSUMIDOR_FINAL"] = "CF";
109
+ SRI_DOCUMENT_TYPE_CUSTOM["IDENTIFICACION_EXTERIOR"] = "IE";
110
+ })(exports.SRI_DOCUMENT_TYPE_CUSTOM || (exports.SRI_DOCUMENT_TYPE_CUSTOM = {}));
111
+ exports.SEPARATOR_KEY_CODE = void 0;
112
+ (function (SEPARATOR_KEY_CODE) {
113
+ SEPARATOR_KEY_CODE["SLASH"] = "|";
114
+ SEPARATOR_KEY_CODE["PUNTO_COMA"] = ";";
115
+ SEPARATOR_KEY_CODE["DOS_PUNTOS"] = ":";
116
+ })(exports.SEPARATOR_KEY_CODE || (exports.SEPARATOR_KEY_CODE = {}));
117
+ const SEPARADORES_REGEX = new RegExp(`[${Object.values(exports.SEPARATOR_KEY_CODE).join('')}]`);
118
+
119
+ const isSuccessResponse = response => response?.status === 'success';
120
+ const isErrorResponse = response => response?.status === 'error';
121
+
122
+ class LegacyApiResponse {
123
+ constructor(code = '0', message = '', payload = null) {
124
+ this.code = code;
125
+ this.message = message;
126
+ this.payload = payload;
127
+ }
128
+ }
129
+
130
+ class PaginationParams {
131
+ constructor(params) {
132
+ this._pageIndex = 1;
133
+ this._pageSize = 10;
134
+ this.sortDirection = 'asc';
135
+ if (params) {
136
+ this.pageIndex = params.pageIndex ?? 1;
137
+ this.pageSize = params.pageSize ?? 10;
138
+ this.sortBy = params.sortBy;
139
+ this.sortDirection = params.sortDirection ?? 'asc';
140
+ this.searchTerm = params.searchTerm;
141
+ this.filters = params.filters;
142
+ }
143
+ }
144
+ get pageIndex() {
145
+ return this._pageIndex;
146
+ }
147
+ set pageIndex(value) {
148
+ this._pageIndex = value < 1 ? 1 : value;
149
+ }
150
+ get pageSize() {
151
+ return this._pageSize;
152
+ }
153
+ set pageSize(value) {
154
+ this._pageSize = value < 1 ? 10 : value > 1000 ? 1000 : value;
155
+ }
156
+ get skip() {
157
+ return (this.pageIndex - 1) * this.pageSize;
158
+ }
159
+ get take() {
160
+ return this.pageSize;
161
+ }
162
+ get isEmpty() {
163
+ return (!this.searchTerm || this.searchTerm.trim() === '') && (!this.filters || Object.keys(this.filters).length === 0);
164
+ }
165
+ buildFilters() {
166
+ if (!this.filters || Object.keys(this.filters).length === 0) return null;
167
+ return {
168
+ ...this.filters
169
+ };
170
+ }
171
+ buildFiltersWithPrefix(prefix) {
172
+ if (!this.filters || Object.keys(this.filters).length === 0) return null;
173
+ const result = {};
174
+ for (const key in this.filters) {
175
+ result[prefix + key] = this.filters[key];
176
+ }
177
+ return result;
178
+ }
179
+ buildSqlParameters() {
180
+ return this.buildFiltersWithPrefix('@');
181
+ }
182
+ }
183
+
184
+ class PricingCalculationError extends Error {
185
+ constructor(operation, originalError) {
186
+ super(`Pricing calculation failed for operation: ${operation}. ${originalError?.message || ''}`);
187
+ this.name = 'PricingCalculationError';
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Calculadora especializada para descuentos
193
+ */
194
+ class DiscountCalculator {
195
+ constructor(decimales = 4) {
196
+ this.decimales = decimales;
197
+ }
198
+ /**
199
+ * Calcula el valor del descuento a partir del porcentaje
200
+ * @param originalPrice - Precio antes del descuento
201
+ * @param discountPercentage - Porcentaje de descuento a aplicar (0-100)
202
+ * @returns Valor del descuento en moneda
203
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
204
+ * @example
205
+ * ```typescript
206
+ * const valorDescuento = calculadora.calculateDiscountAmount(100, 10); // 10
207
+ * ```
208
+ */
209
+ calculateDiscountAmount(originalPrice, discountPercentage) {
210
+ try {
211
+ utils.ParameterValidator.validatePositiveNumber(originalPrice, 'originalPrice');
212
+ utils.ParameterValidator.validatePercentage(discountPercentage, 'discountPercentage');
213
+ const percentage = utils.DecimalConverter.divideAsNumber(discountPercentage, 100);
214
+ return utils.DecimalConverter.multiplyAsNumber(originalPrice, percentage);
215
+ } catch (error) {
216
+ throw new PricingCalculationError('calculateDiscountAmount', error);
217
+ }
218
+ }
219
+ /**
220
+ * Aplica un descuento al precio original
221
+ * @param originalPrice - Precio antes del descuento
222
+ * @param discountPercentage - Porcentaje de descuento a aplicar
223
+ * @returns Precio final después del descuento
224
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
225
+ * @example
226
+ * ```typescript
227
+ * const precioConDescuento = calculadora.applyDiscount(100, 10); // 90
228
+ * ```
229
+ */
230
+ applyDiscount(originalPrice, discountPercentage) {
231
+ try {
232
+ const discountAmount = this.calculateDiscountAmount(originalPrice, discountPercentage);
233
+ return utils.DecimalConverter.subtractAsNumber(originalPrice, discountAmount);
234
+ } catch (error) {
235
+ throw new PricingCalculationError('applyDiscount', error);
236
+ }
237
+ }
238
+ /**
239
+ * Calcula el porcentaje de descuento a partir de los valores
240
+ * @param originalPrice - Precio antes del descuento
241
+ * @param discountAmount - Valor del descuento aplicado
242
+ * @returns Porcentaje de descuento
243
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
244
+ * @example
245
+ * ```typescript
246
+ * const porcentaje = calculadora.calculateDiscountPercentage(100, 10); // 10
247
+ * ```
248
+ */
249
+ calculateDiscountPercentage(originalPrice, discountAmount) {
250
+ try {
251
+ utils.ParameterValidator.validatePositiveNumber(originalPrice, 'originalPrice');
252
+ utils.ParameterValidator.validatePositiveNumber(discountAmount, 'discountAmount');
253
+ if (discountAmount > originalPrice) {
254
+ throw new utils.InvalidParameterError('discountAmount', 'No puede ser mayor que el precio original');
255
+ }
256
+ const percentage = utils.DecimalConverter.divideAsNumber(discountAmount, originalPrice);
257
+ return utils.DecimalConverter.multiplyAsNumber(percentage, 100);
258
+ } catch (error) {
259
+ throw new PricingCalculationError('calculateDiscountPercentage', error);
260
+ }
261
+ }
262
+ /**
263
+ * Obtiene los detalles completos del cálculo de descuento
264
+ * @param originalPrice - Precio antes del descuento
265
+ * @param discountPercentage - Porcentaje de descuento a aplicar
266
+ * @returns Objeto con todos los detalles del cálculo
267
+ * @example
268
+ * ```typescript
269
+ * const detalles = calculadora.getDiscountCalculationDetails(100, 10);
270
+ * // { originalPrice: 100, discountRate: 10, discountAmount: 10, finalPrice: 90 }
271
+ * ```
272
+ */
273
+ getDiscountCalculationDetails(originalPrice, discountPercentage) {
274
+ const discountAmount = this.calculateDiscountAmount(originalPrice, discountPercentage);
275
+ const finalPrice = this.applyDiscount(originalPrice, discountPercentage);
276
+ return {
277
+ originalPrice,
278
+ discountRate: discountPercentage,
279
+ discountAmount,
280
+ finalPrice
281
+ };
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Calculadora especializada para líneas de productos/servicios en facturas
287
+ */
288
+ class LineItemCalculator {
289
+ constructor(decimales = 4) {
290
+ this.decimales = decimales;
291
+ }
292
+ /**
293
+ * Calcula el total de una línea de producto incluyendo descuentos e impuestos
294
+ * @param unitPrice - Precio por unidad del producto
295
+ * @param quantity - Cantidad de productos
296
+ * @param discountAmount - Valor del descuento a aplicar (por defecto 0)
297
+ * @param taxAmount - Valor del impuesto a aplicar (por defecto 0)
298
+ * @returns Objeto con todos los cálculos de la línea
299
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
300
+ * @example
301
+ * ```typescript
302
+ * const linea = calculadora.calcularTotalLinea(50, 2, 10, 8.4);
303
+ * // Resultado: subtotal: 100, con descuento: 90, total con impuesto: 98.4
304
+ * ```
305
+ */
306
+ calculateLineItemTotal(unitPrice, quantity, discountAmount = 0, taxAmount = 0) {
307
+ try {
308
+ utils.ParameterValidator.validatePositiveNumber(unitPrice, 'unitPrice');
309
+ utils.ParameterValidator.validatePositiveNumber(quantity, 'quantity');
310
+ utils.ParameterValidator.validatePositiveNumber(discountAmount, 'discountAmount');
311
+ utils.ParameterValidator.validatePositiveNumber(taxAmount, 'taxAmount');
312
+ const subtotal = utils.DecimalConverter.multiplyAsNumber(unitPrice, quantity);
313
+ const subtotalAfterDiscount = utils.DecimalConverter.subtractAsNumber(subtotal, discountAmount);
314
+ const total = utils.DecimalConverter.addAsNumber(subtotalAfterDiscount, taxAmount);
315
+ return {
316
+ unitPrice,
317
+ quantity,
318
+ subtotal,
319
+ discountAmount,
320
+ subtotalAfterDiscount,
321
+ taxAmount,
322
+ total
323
+ };
324
+ } catch (error) {
325
+ throw new PricingCalculationError('calcularTotalLinea', error);
326
+ }
327
+ }
328
+ /**
329
+ * Calcula el subtotal antes de aplicar descuentos
330
+ * @param unitPrice - Precio por unidad
331
+ * @param quantity - Cantidad de productos
332
+ * @returns Subtotal calculado
333
+ * @example
334
+ * ```typescript
335
+ * const subtotal = calculadora.calculateSubtotal(25, 4); // 100
336
+ * ```
337
+ */
338
+ calculateSubtotal(unitPrice, quantity) {
339
+ return utils.DecimalConverter.multiplyAsNumber(unitPrice, quantity);
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Calculadora especializada para márgenes de ganancia y utilidades
345
+ */
346
+ class ProfitCalculator {
347
+ constructor(decimals = 4) {
348
+ this.decimals = decimals;
349
+ }
350
+ /**
351
+ * Calcula el precio de venta a partir del costo y el margen de ganancia
352
+ * @param cost - Costo del producto o servicio
353
+ * @param profitMarginPercentage - Porcentaje de margen de ganancia deseado
354
+ * @returns Precio de venta calculado
355
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
356
+ * @example
357
+ * ```typescript
358
+ * const precioVenta = calculadora.calculateSalePriceFromMargin(80, 25); // 100
359
+ * ```
360
+ */
361
+ calculateSalePriceFromMargin(cost, profitMarginPercentage) {
362
+ try {
363
+ utils.ParameterValidator.validatePositiveNumber(cost, 'cost');
364
+ utils.ParameterValidator.validatePositiveNumber(profitMarginPercentage, 'profitMarginPercentage');
365
+ const marginMultiplier = utils.DecimalConverter.divideAsNumber(profitMarginPercentage, 100);
366
+ const profitAmount = utils.DecimalConverter.multiplyAsNumber(cost, marginMultiplier);
367
+ return utils.DecimalConverter.addAsNumber(cost, profitAmount);
368
+ } catch (error) {
369
+ throw new PricingCalculationError('calculateSalePriceFromMargin', error);
370
+ }
371
+ }
372
+ /**
373
+ * Calcula el porcentaje de margen de ganancia a partir del precio de venta y costo
374
+ * @param salePrice - Precio de venta del producto
375
+ * @param cost - Costo del producto
376
+ * @returns Porcentaje de margen de ganancia
377
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
378
+ * @example
379
+ * ```typescript
380
+ * const margen = calculadora.calculateProfitMarginPercentage(100, 80); // 25
381
+ * ```
382
+ */
383
+ calculateProfitMarginPercentage(salePrice, cost) {
384
+ try {
385
+ utils.ParameterValidator.validatePositiveNumber(salePrice, 'salePrice');
386
+ utils.ParameterValidator.validatePositiveNumber(cost, 'cost');
387
+ if (salePrice < cost) {
388
+ throw new utils.InvalidParameterError('salePrice', 'El precio de venta no puede ser menor que el costo');
389
+ }
390
+ const profitAmount = utils.DecimalConverter.subtractAsNumber(salePrice, cost);
391
+ const profitRatio = utils.DecimalConverter.divideAsNumber(profitAmount, cost);
392
+ return utils.DecimalConverter.multiplyAsNumber(profitRatio, 100);
393
+ } catch (error) {
394
+ throw new PricingCalculationError('calculateProfitMarginPercentage', error);
395
+ }
396
+ }
397
+ /**
398
+ * Calcula el valor de la ganancia
399
+ * @param salePrice - Precio de venta
400
+ * @param cost - Costo del producto
401
+ * @returns Valor de la ganancia obtenida
402
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
403
+ * @example
404
+ * ```typescript
405
+ * const ganancia = calculadora.calculateProfitAmount(100, 80); // 20
406
+ * ```
407
+ */
408
+ calculateProfitAmount(salePrice, cost) {
409
+ try {
410
+ utils.ParameterValidator.validatePositiveNumber(salePrice, 'salePrice');
411
+ utils.ParameterValidator.validatePositiveNumber(cost, 'cost');
412
+ return utils.DecimalConverter.subtractAsNumber(salePrice, cost);
413
+ } catch (error) {
414
+ throw new PricingCalculationError('calculateProfitAmount', error);
415
+ }
416
+ }
417
+ /**
418
+ * Obtiene los detalles completos del cálculo de ganancia
419
+ * @param salePrice - Precio de venta
420
+ * @param cost - Costo del producto
421
+ * @returns Objeto con todos los detalles del cálculo
422
+ * @example
423
+ * ```typescript
424
+ * const detalles = calculadora.obtenerDetallesCalculoGanancia(100, 80);
425
+ * // { salePrice: 100, cost: 80, profitAmount: 20, profitMargin: 25 }
426
+ * ```
427
+ */
428
+ getProfitCalculationDetails(salePrice, cost) {
429
+ const profitAmount = this.calculateProfitAmount(salePrice, cost);
430
+ const profitMargin = this.calculateProfitMarginPercentage(salePrice, cost);
431
+ return {
432
+ salePrice,
433
+ cost,
434
+ profitAmount,
435
+ profitMargin
436
+ };
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Calculadora especializada para impuestos (IVA, IGV, etc.)
442
+ */
443
+ class TaxCalculator {
444
+ constructor(decimales = 4) {
445
+ this.decimales = decimales;
446
+ }
447
+ /**
448
+ * Calcula el valor del impuesto basado en el precio base y la tasa
449
+ * @param basePrice - Precio sin impuestos
450
+ * @param taxRate - Porcentaje del impuesto (ej: 21 para 21%)
451
+ * @returns Valor del impuesto calculado
452
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
453
+ * @example
454
+ * ```typescript
455
+ * const calculadora = new TaxCalculator();
456
+ * const valorIva = calculadora.calculateTaxAmount(100, 21); // 21
457
+ * ```
458
+ */
459
+ calculateTaxAmount(basePrice, taxRate) {
460
+ try {
461
+ utils.ParameterValidator.validatePositiveNumber(basePrice, 'basePrice');
462
+ utils.ParameterValidator.validatePositiveNumber(taxRate, 'taxRate');
463
+ const multiplicador = utils.DecimalConverter.multiplyAsNumber(basePrice, taxRate);
464
+ return utils.DecimalConverter.divideAsNumber(multiplicador, 100);
465
+ } catch (error) {
466
+ throw new PricingCalculationError('calculateTaxAmount', error);
467
+ }
468
+ }
469
+ /**
470
+ * Calcula el precio incluyendo impuestos
471
+ * @param basePrice - Precio sin impuestos
472
+ * @param taxRate - Porcentaje del impuesto
473
+ * @returns Precio total con impuestos incluidos
474
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
475
+ * @example
476
+ * ```typescript
477
+ * const precioConIva = calculadora.calculatePriceWithTax(100, 21); // 121
478
+ * ```
479
+ */
480
+ calculatePriceWithTax(basePrice, taxRate) {
481
+ try {
482
+ const taxAmount = this.calculateTaxAmount(basePrice, taxRate);
483
+ return utils.DecimalConverter.addAsNumber(basePrice, taxAmount);
484
+ } catch (error) {
485
+ throw new PricingCalculationError('calculatePriceWithTax', error);
486
+ }
487
+ }
488
+ /**
489
+ * Calcula el precio base a partir de un precio que ya incluye impuestos
490
+ * @param priceWithTax - Precio que incluye impuestos
491
+ * @param taxRate - Porcentaje del impuesto aplicado
492
+ * @returns Precio base sin impuestos
493
+ * @throws {PricingCalculationError} Si ocurre un error en el cálculo
494
+ * @example
495
+ * ```typescript
496
+ * const precioBase = calculadora.calculateBasePriceFromTaxIncluded(121, 21); // 100
497
+ * ```
498
+ */
499
+ calculateBasePriceFromTaxIncluded(priceWithTax, taxRate) {
500
+ try {
501
+ utils.ParameterValidator.validatePositiveNumber(priceWithTax, 'priceWithTax');
502
+ utils.ParameterValidator.validatePositiveNumber(taxRate, 'taxRate');
503
+ const taxMultiplier = utils.DecimalConverter.divideAsNumber(taxRate, 100);
504
+ const divisor = utils.DecimalConverter.addAsNumber(1, taxMultiplier);
505
+ return utils.DecimalConverter.divideAsNumber(priceWithTax, divisor);
506
+ } catch (error) {
507
+ throw new PricingCalculationError('calculateBasePriceFromTaxIncluded', error);
508
+ }
509
+ }
510
+ /**
511
+ * Obtiene los detalles completos del cálculo de impuestos
512
+ * @param basePrice - Precio sin impuestos
513
+ * @param taxRate - Porcentaje del impuesto
514
+ * @returns Objeto con todos los detalles del cálculo
515
+ * @example
516
+ * ```typescript
517
+ * const detalles = calculadora.getTaxCalculationDetails(100, 21);
518
+ * // { montoBase: 100, taxRate: 21, valorImpuesto: 21, montoTotal: 121 }
519
+ * ```
520
+ */
521
+ getTaxCalculationDetails(basePrice, taxRate) {
522
+ const taxAmount = this.calculateTaxAmount(basePrice, taxRate);
523
+ const totalAmount = this.calculatePriceWithTax(basePrice, taxRate);
524
+ return {
525
+ baseAmount: basePrice,
526
+ taxRate,
527
+ taxAmount,
528
+ totalAmount
529
+ };
530
+ }
531
+ }
532
+
533
+ class PricingCalculator {
534
+ constructor(config = {}) {
535
+ this.config = {
536
+ defaultDecimals: config.defaultDecimals ?? 4,
537
+ roundingMode: config.roundingMode ?? 'round',
538
+ errorHandling: config.errorHandling ?? 'throw'
539
+ };
540
+ this.tax = new TaxCalculator(this.config.defaultDecimals);
541
+ this.discount = new DiscountCalculator(this.config.defaultDecimals);
542
+ this.profit = new ProfitCalculator(this.config.defaultDecimals);
543
+ this.lineItem = new LineItemCalculator(this.config.defaultDecimals);
544
+ }
545
+ updateConfig(newConfig) {
546
+ this.config = {
547
+ ...this.config,
548
+ ...newConfig
549
+ };
550
+ }
551
+ getConfig() {
552
+ return {
553
+ ...this.config
554
+ };
555
+ }
556
+ }
557
+
558
+ // src/domain/value-objects/base.vo.ts
559
+ class BaseVo {
560
+ constructor(value) {
561
+ this.value = value;
562
+ this.validate(value);
563
+ }
564
+ getValue() {
565
+ return this.value;
566
+ }
567
+ equals(other) {
568
+ return JSON.stringify(this.value) === JSON.stringify(other.value);
569
+ }
570
+ }
571
+
572
+ class EntityIdVo extends BaseVo {
573
+ constructor(value) {
574
+ super(value);
575
+ }
576
+ static generate() {
577
+ return new EntityIdVo(uuid.v4());
578
+ }
579
+ validate(value) {
580
+ if (!value || value.trim().length === 0) {
581
+ throw new Error('EntityIdVo cannot be empty');
582
+ }
583
+ }
584
+ }
585
+
586
+ // src/domain/value-objects/money.vo.ts
587
+ class MoneyVo extends BaseVo {
588
+ constructor(amount, currency = 'USD') {
589
+ super({
590
+ amount,
591
+ currency
592
+ });
593
+ }
594
+ validate(value) {
595
+ if (value.amount < 0) throw new Error('Amount cannot be negative');
596
+ if (!value.currency || value.currency.length !== 3) {
597
+ throw new Error('Invalid currency code');
598
+ }
599
+ }
600
+ add(other) {
601
+ if (this.value.currency !== other.value.currency) {
602
+ throw new Error('Cannot add different currencies');
603
+ }
604
+ return new MoneyVo(this.value.amount + other.value.amount, this.value.currency);
605
+ }
606
+ }
607
+
608
+ class IdentificationNumberVo extends BaseVo {
609
+ constructor(id) {
610
+ super(id);
611
+ this.id = id?.trim() || '';
612
+ this.type = this.determineType();
613
+ if (!this.validate()) {
614
+ throw new Error(`Número de identificación inválido: ${this.id}`);
615
+ }
616
+ }
617
+ determineType() {
618
+ if (this.id.length === 10) return exports.SRI_DOCUMENT_TYPE.CEDULA;
619
+ if (this.id.length === 13) return exports.SRI_DOCUMENT_TYPE.RUC;
620
+ throw new Error('Número de identificación debe tener 10 o 13 dígitos');
621
+ }
622
+ validate() {
623
+ if (this.type === exports.SRI_DOCUMENT_TYPE.CEDULA) {
624
+ return this.isValidCedulaAlgorithm();
625
+ }
626
+ if (this.type === exports.SRI_DOCUMENT_TYPE.RUC) {
627
+ return this.isValidRucAlgorithm();
628
+ }
629
+ return false;
630
+ }
631
+ // Validación completa de cédula ecuatoriana con algoritmo
632
+ isValidCedulaAlgorithm() {
633
+ if (!/^\d{10}$/.test(this.id)) return false;
634
+ // Verificar que los primeros 2 dígitos sean una provincia válida (01-24)
635
+ const provincia = parseInt(this.id.substring(0, 2));
636
+ if (provincia < 1 || provincia > 24) return false;
637
+ // Algoritmo de validación del dígito verificador
638
+ const coeficientes = [2, 1, 2, 1, 2, 1, 2, 1, 2];
639
+ let suma = 0;
640
+ for (let i = 0; i < 9; i++) {
641
+ const valor = parseInt(this.id.charAt(i)) * coeficientes[i];
642
+ suma += valor > 9 ? valor - 9 : valor;
643
+ }
644
+ const digitoVerificador = suma % 10 === 0 ? 0 : 10 - suma % 10;
645
+ return digitoVerificador === parseInt(this.id.charAt(9));
646
+ }
647
+ // Validación de RUC más flexible
648
+ isValidRucAlgorithm() {
649
+ if (!/^\d{13}$/.test(this.id)) return false;
650
+ // Los primeros 10 dígitos deben ser una cédula válida para personas naturales
651
+ // o seguir reglas específicas para sociedades
652
+ const tercerDigito = parseInt(this.id.charAt(2));
653
+ // RUC de persona natural (tercer dígito < 6)
654
+ if (tercerDigito < 6) {
655
+ const cedula = this.id.substring(0, 10);
656
+ const tempCedula = IdentificationNumberVo.validateCedula(cedula);
657
+ return tempCedula && this.id.endsWith('001');
658
+ }
659
+ // RUC de sociedad privada (tercer dígito = 9)
660
+ if (tercerDigito === 9) {
661
+ return this.isValidSociedadRuc();
662
+ }
663
+ // RUC de entidad pública (tercer dígito = 6)
664
+ if (tercerDigito === 6) {
665
+ return this.id.endsWith('0001');
666
+ }
667
+ return false;
668
+ }
669
+ isValidSociedadRuc() {
670
+ const coeficientes = [4, 3, 2, 7, 6, 5, 4, 3, 2];
671
+ let suma = 0;
672
+ for (let i = 0; i < 9; i++) {
673
+ suma += parseInt(this.id.charAt(i)) * coeficientes[i];
674
+ }
675
+ const residuo = suma % 11;
676
+ const digitoVerificador = residuo === 0 ? 0 : 11 - residuo;
677
+ return digitoVerificador === parseInt(this.id.charAt(9)) && this.id.endsWith('001');
678
+ }
679
+ // Método auxiliar estático para validar cédula sin crear instancia
680
+ static validateCedula(cedula) {
681
+ if (!/^\d{10}$/.test(cedula)) return false;
682
+ const provincia = parseInt(cedula.substring(0, 2));
683
+ if (provincia < 1 || provincia > 24) return false;
684
+ const coeficientes = [2, 1, 2, 1, 2, 1, 2, 1, 2];
685
+ let suma = 0;
686
+ for (let i = 0; i < 9; i++) {
687
+ const valor = parseInt(cedula.charAt(i)) * coeficientes[i];
688
+ suma += valor > 9 ? valor - 9 : valor;
689
+ }
690
+ const digitoVerificador = suma % 10 === 0 ? 0 : 10 - suma % 10;
691
+ return digitoVerificador === parseInt(cedula.charAt(9));
692
+ }
693
+ // Métodos públicos mejorados
694
+ isValidCedula() {
695
+ return this.type === exports.SRI_DOCUMENT_TYPE.CEDULA && this.isValidCedulaAlgorithm();
696
+ }
697
+ isValidRuc() {
698
+ return this.type === exports.SRI_DOCUMENT_TYPE.RUC && this.isValidRucAlgorithm();
699
+ }
700
+ getId() {
701
+ return this.id;
702
+ }
703
+ getType() {
704
+ return this.type;
705
+ }
706
+ // Método estático mejorado
707
+ static tryCreate(id) {
708
+ try {
709
+ new IdentificationNumberVo(id);
710
+ return true;
711
+ } catch {
712
+ return false;
713
+ }
714
+ }
715
+ // Método adicional para obtener información del tipo de RUC
716
+ getRucType() {
717
+ if (this.type !== exports.SRI_DOCUMENT_TYPE.RUC) return null;
718
+ const tercerDigito = parseInt(this.id.charAt(2));
719
+ if (tercerDigito < 6) return 'Persona Natural';
720
+ if (tercerDigito === 6) return 'Entidad Pública';
721
+ if (tercerDigito === 9) return 'Sociedad Privada';
722
+ return 'Desconocido';
723
+ }
724
+ }
725
+
726
+ exports.AxiosAdapter = AxiosAdapter;
727
+ exports.BaseVo = BaseVo;
728
+ exports.DiscountCalculator = DiscountCalculator;
729
+ exports.EntityIdVo = EntityIdVo;
730
+ exports.FetchAdapter = FetchAdapter;
731
+ exports.HttpClientFactory = HttpClientFactory;
732
+ exports.IdentificationNumberVo = IdentificationNumberVo;
733
+ exports.LegacyApiResponse = LegacyApiResponse;
734
+ exports.LineItemCalculator = LineItemCalculator;
735
+ exports.MoneyVo = MoneyVo;
736
+ exports.PaginationParams = PaginationParams;
737
+ exports.PricingCalculator = PricingCalculator;
738
+ exports.ProfitCalculator = ProfitCalculator;
739
+ exports.SEPARADORES_REGEX = SEPARADORES_REGEX;
740
+ exports.TaxCalculator = TaxCalculator;
741
+ exports.isErrorResponse = isErrorResponse;
742
+ exports.isSuccessResponse = isSuccessResponse;