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