softseti-sale-calculator-library 4.0.1 → 4.1.1
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/package.json +1 -1
- package/src/core/rule-engine.js +11 -8
- package/src/core/sale-calculator.js +24 -42
package/package.json
CHANGED
package/src/core/rule-engine.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
// src/rules/RuleEngine.js
|
|
2
|
-
const { ZenEngine } = require('@gorules/zen-engine');
|
|
3
|
-
|
|
4
2
|
class RuleEngine {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.engine = new
|
|
3
|
+
constructor(zenEngine) {
|
|
4
|
+
this.engine = zenEngine ? new zenEngine() : null;
|
|
7
5
|
this.ruleCache = new Map();
|
|
8
6
|
}
|
|
9
7
|
|
|
@@ -15,6 +13,10 @@ class RuleEngine {
|
|
|
15
13
|
*/
|
|
16
14
|
async evaluateRule(ruleContent, context) {
|
|
17
15
|
try {
|
|
16
|
+
// Verificar si el motor está disponible
|
|
17
|
+
if (!this.engine) {
|
|
18
|
+
throw new Error('Rule engine not initialized. ZenEngine dependency not provided.');
|
|
19
|
+
}
|
|
18
20
|
|
|
19
21
|
const decision = this.engine.createDecision(ruleContent);
|
|
20
22
|
const result = await decision.safeEvaluate(context);
|
|
@@ -34,16 +36,17 @@ class RuleEngine {
|
|
|
34
36
|
* @returns {Promise<Array>} Array of rule execution results
|
|
35
37
|
*/
|
|
36
38
|
async executeRules(rules, context) {
|
|
39
|
+
if (!this.engine || !rules || rules.length === 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
const results = [];
|
|
38
44
|
|
|
39
45
|
for (const rule of rules) {
|
|
40
46
|
try {
|
|
41
47
|
const result = await this.evaluateRule(rule.rule_content, context);
|
|
42
|
-
results.push(
|
|
43
|
-
result
|
|
44
|
-
);
|
|
48
|
+
results.push(result);
|
|
45
49
|
} catch (error) {
|
|
46
|
-
|
|
47
50
|
results.push({
|
|
48
51
|
ruleId: rule.id,
|
|
49
52
|
success: false,
|
|
@@ -19,6 +19,8 @@ let useRounding = false;
|
|
|
19
19
|
|
|
20
20
|
let preprocessedSale = {};
|
|
21
21
|
|
|
22
|
+
let zenEngineDependency = null;
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
25
|
* Sales Calculation Engine (V1)
|
|
24
26
|
* @author softseti
|
|
@@ -39,13 +41,13 @@ let preprocessedSale = {};
|
|
|
39
41
|
* - settings: GlobalSettings
|
|
40
42
|
*/
|
|
41
43
|
|
|
42
|
-
function init(params) {
|
|
44
|
+
function init(params, dependencies = {}) {
|
|
43
45
|
products = params.products || {};
|
|
44
46
|
productTaxes = params.product_taxes || {};
|
|
45
47
|
taxes = params.taxes || {};
|
|
46
48
|
wholesaleLevels = params.wholesale_levels || {};
|
|
47
49
|
settings = params.settings || {};
|
|
48
|
-
// Set currencies
|
|
50
|
+
// Set currencies conversion
|
|
49
51
|
exchangeRates = params.exchange_rates || {};
|
|
50
52
|
currencyConverterRates = params.currency_converter_rates || {};
|
|
51
53
|
currencies = params.currencies || {};
|
|
@@ -56,6 +58,11 @@ function init(params) {
|
|
|
56
58
|
|
|
57
59
|
useRounding = params.use_rounding !== undefined ? params.use_rounding : false;
|
|
58
60
|
|
|
61
|
+
// Store dependencies
|
|
62
|
+
if (dependencies.zenEngine) {
|
|
63
|
+
zenEngineDependency = dependencies.zenEngine;
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
// Start engine
|
|
60
67
|
initializeRuleSystem(businessRules);
|
|
61
68
|
|
|
@@ -67,20 +74,19 @@ function init(params) {
|
|
|
67
74
|
|
|
68
75
|
function initializeRuleSystem(businessRules) {
|
|
69
76
|
try {
|
|
70
|
-
ruleEngine = new RuleEngine();
|
|
77
|
+
ruleEngine = new RuleEngine(zenEngineDependency);
|
|
71
78
|
ruleManager = new RuleManager();
|
|
72
79
|
|
|
73
|
-
if (businessRules && Object.keys(businessRules).length > 0) {
|
|
74
|
-
|
|
80
|
+
if (businessRules && Object.keys(businessRules).length > 0) {
|
|
75
81
|
ruleManager.loadRules(businessRules);
|
|
76
|
-
console.log('
|
|
82
|
+
console.log('Rule system initialized successfully');
|
|
77
83
|
|
|
78
84
|
} else {
|
|
79
|
-
console.warn('No
|
|
85
|
+
console.warn('No business rules found to load');
|
|
80
86
|
}
|
|
81
87
|
} catch (error) {
|
|
82
|
-
console.error('Error
|
|
83
|
-
// Fallback:
|
|
88
|
+
console.error('Error initializing rule system:', error);
|
|
89
|
+
// Fallback: without rules
|
|
84
90
|
ruleEngine = null;
|
|
85
91
|
ruleManager = null;
|
|
86
92
|
}
|
|
@@ -94,10 +100,10 @@ function initializeRuleSystem(businessRules) {
|
|
|
94
100
|
function validateSaleStructure(sale) {
|
|
95
101
|
const required = ['receipt_type_key', 'company_id', 'branch_id', 'dwSaleProducts'];
|
|
96
102
|
if (required.some(field => !sale[field])) {
|
|
97
|
-
throw new Error("
|
|
103
|
+
throw new Error("Invalid sale structure");
|
|
98
104
|
}
|
|
99
105
|
if (Object.keys(products).length === 0) {
|
|
100
|
-
throw new Error("
|
|
106
|
+
throw new Error("Products not loaded");
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -279,7 +285,7 @@ function calcDwSaleProductPrice(saleProduct, sale) {
|
|
|
279
285
|
|
|
280
286
|
const saleCurrency = sale.currency_iso;
|
|
281
287
|
|
|
282
|
-
//
|
|
288
|
+
// Currency conversion
|
|
283
289
|
if (productCurrency && saleCurrency && productCurrency !== saleCurrency) {
|
|
284
290
|
return exchange(
|
|
285
291
|
basePrice,
|
|
@@ -287,7 +293,7 @@ function calcDwSaleProductPrice(saleProduct, sale) {
|
|
|
287
293
|
saleCurrency
|
|
288
294
|
);
|
|
289
295
|
}
|
|
290
|
-
// If
|
|
296
|
+
// If no exchange or conversion rate exists, return the basePrice.
|
|
291
297
|
return basePrice;
|
|
292
298
|
}
|
|
293
299
|
|
|
@@ -319,7 +325,7 @@ async function getApplicableProductTaxes(saleProduct, productSum, sale) {
|
|
|
319
325
|
// GET General Taxes
|
|
320
326
|
const generalTaxes = getGeneralTaxes(sale);
|
|
321
327
|
|
|
322
|
-
//
|
|
328
|
+
// Combine all applicable taxes
|
|
323
329
|
const taxes = [...specificTaxes, ...generalTaxes];
|
|
324
330
|
|
|
325
331
|
// Get rules for taxes
|
|
@@ -472,14 +478,14 @@ function getTaxReceiptTypeKey(receiptType) {
|
|
|
472
478
|
function exchange(amount, fromCurrency, toCurrency) {
|
|
473
479
|
if (fromCurrency === toCurrency) return roundCurrency(amount);
|
|
474
480
|
|
|
475
|
-
// GET
|
|
481
|
+
// GET EXCHANGE RATE
|
|
476
482
|
const exchangeRateModel = findExchangeRate(fromCurrency, toCurrency);
|
|
477
483
|
|
|
478
484
|
if (exchangeRateModel) {
|
|
479
485
|
return roundCurrency(applyExchangeStrategy(amount, exchangeRateModel));
|
|
480
486
|
}
|
|
481
487
|
|
|
482
|
-
// GET CURRENCY CONVERTER RATE WHEN
|
|
488
|
+
// GET CURRENCY CONVERTER RATE WHEN NO EXCHANGE RATE EXISTS
|
|
483
489
|
const currencyConverterRateModel = findCurrencyConverterRate(fromCurrency, toCurrency);
|
|
484
490
|
|
|
485
491
|
if (!currencyConverterRateModel) {
|
|
@@ -706,7 +712,7 @@ function appliedWholesaleLevels(sale) {
|
|
|
706
712
|
}
|
|
707
713
|
|
|
708
714
|
const result = [];
|
|
709
|
-
let levelCounter = 1; //
|
|
715
|
+
let levelCounter = 1; // For generating sequential IDs as in the example
|
|
710
716
|
|
|
711
717
|
sale.dwSaleProducts.forEach(product => {
|
|
712
718
|
const productData = products[product.product_id];
|
|
@@ -995,30 +1001,6 @@ function getConsolidatedTaxes(sale) {
|
|
|
995
1001
|
.sort((a, b) => b.amount - a.amount);
|
|
996
1002
|
}
|
|
997
1003
|
|
|
998
|
-
/**
|
|
999
|
-
* Rounds or truncates currency values based on configuration
|
|
1000
|
-
* @param {number} value - Value to process
|
|
1001
|
-
* @param {boolean} overrideRounding - Optional override for rounding behavior
|
|
1002
|
-
* @returns {number} Processed value
|
|
1003
|
-
*
|
|
1004
|
-
function roundCurrency(value, overrideRounding) {
|
|
1005
|
-
if (typeof value !== 'number' || isNaN(value)) {
|
|
1006
|
-
return 0;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// Determine rounding behavior (parameter override > global setting > default false)
|
|
1010
|
-
const shouldRound = overrideRounding !== undefined ? overrideRounding : useRounding;
|
|
1011
|
-
|
|
1012
|
-
if (shouldRound) {
|
|
1013
|
-
// Use rounding (comportamiento original)
|
|
1014
|
-
return Math.round(value * roundingFactor) / roundingFactor;
|
|
1015
|
-
} else {
|
|
1016
|
-
// Use truncation (comportamiento por defecto - sin redondeo)
|
|
1017
|
-
return Math.trunc(value * roundingFactor) / roundingFactor;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
*/
|
|
1021
|
-
|
|
1022
1004
|
/**
|
|
1023
1005
|
* Rounds or truncates currency values based on configuration
|
|
1024
1006
|
* @param {number} value - Value to process
|
|
@@ -1073,4 +1055,4 @@ module.exports = {
|
|
|
1073
1055
|
getApplicableProductTaxes, // Async
|
|
1074
1056
|
exchange
|
|
1075
1057
|
}
|
|
1076
|
-
};
|
|
1058
|
+
};
|