softseti-sale-calculator-library 3.7.0 → 3.7.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softseti-sale-calculator-library",
3
- "version": "3.7.0",
3
+ "version": "3.7.2",
4
4
  "description": "Sales calculation engine by Softseti",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -8,7 +8,7 @@ let currencyConverterRates = {};
8
8
  let currencies = {};
9
9
  let decimalPlaces = 2;
10
10
  let roundingFactor = 100;
11
- let useRounding = false;
11
+ let useRounding = false;
12
12
 
13
13
  /**
14
14
  * Sales Calculation Engine (V1)
@@ -48,7 +48,7 @@ function init(params) {
48
48
  decimalPlaces = params.decimal_places !== undefined ? params.decimal_places : 2;
49
49
  roundingFactor = Math.pow(10, decimalPlaces);
50
50
 
51
- useRounding = params.use_rounding !== undefined ? params.use_rounding : false;
51
+ useRounding = params.use_rounding !== undefined ? params.use_rounding : false;
52
52
  }
53
53
 
54
54
  // ==============================================
@@ -127,17 +127,30 @@ function preprocessSale(sale) {
127
127
  if (sale.payments) {
128
128
  sale.payments = sale.payments.map(payment => {
129
129
  const originalCurrency = currencies[payment.currency_id];
130
+
131
+ // If payment already has calculated fields, use them
132
+ if (payment.amount && payment.original_amount) {
133
+ return {
134
+ ...payment,
135
+ original_currency_id: payment.currency_id,
136
+ original_currency_iso: originalCurrency?.iso,
137
+ currency_id: sale.currency_id,
138
+ currency_iso: sale.currency_iso,
139
+ change_amount: Math.max(0, payment.gived_amount - payment.amount)
140
+ };
141
+ }
142
+
143
+ // Otherwise calculate from basic payment data
130
144
  return {
131
145
  ...payment,
132
146
  original_currency_id: payment.currency_id,
133
147
  original_currency_iso: originalCurrency?.iso,
134
148
  currency_id: sale.currency_id,
135
- currency_iso: currencies[sale.currency_id]?.iso,
136
- original_gived_amount: payment.gived_amount,
137
- gived_amount: calcGivedPaymentAmount({
149
+ currency_iso: sale.currency_iso,
150
+ original_gived_amount: payment.gived_amount || payment.amount,
151
+ ...calculatePaymentDetails({
138
152
  ...payment,
139
- original_gived_amount: payment.gived_amount,
140
- currency_iso: currencies[sale.currency_id]?.iso,
153
+ original_gived_amount: payment.gived_amount || payment.amount,
141
154
  original_currency_iso: originalCurrency?.iso
142
155
  }, sale)
143
156
  };
@@ -148,7 +161,7 @@ function preprocessSale(sale) {
148
161
  sale.dwSaleProducts = sale.dwSaleProducts.map(product => {
149
162
  const productData = products[product.product_id];
150
163
  const productCurrency = currencies[productData?.currency_id];
151
-
164
+
152
165
  // Calculate the actual price that will be used (including wholesale)
153
166
  const actualPrice = roundCurrency(calcDwSaleProductPrice(product, sale));
154
167
 
@@ -204,7 +217,7 @@ function calcDwSaleProductSum(saleProduct, sale) {
204
217
  //return roundCurrency((saleProduct.quantity * price) / 2); original logic to calc based on legacy
205
218
  // Calc sum for products in relation with price product
206
219
  //return roundCurrency(saleProduct.quantity * price);
207
- return roundCurrency((saleProduct.quantity * price) / baseQuantity);
220
+ return roundCurrency((saleProduct.quantity * price) / baseQuantity);
208
221
  }
209
222
 
210
223
  function calcDwSaleProductPrice(saleProduct, sale) {
@@ -262,14 +275,14 @@ function getApplicableProductTaxes(saleProduct, productSum, sale) {
262
275
  const specificTaxes = getProductSpecificTaxes(saleProduct, sale);
263
276
  // GET General Taxes
264
277
  const generalTaxes = getGeneralTaxes(sale);
265
-
278
+
266
279
  const totalTax = [...specificTaxes, ...generalTaxes].reduce(
267
280
  (total, tax) => {
268
281
  const taxAmount = productSum * (tax.rate / 100);
269
- return total + roundCurrency(taxAmount);
282
+ return total + roundCurrency(taxAmount);
270
283
  }, 0
271
284
  );
272
-
285
+
273
286
  return roundCurrency(totalTax);
274
287
  }
275
288
 
@@ -345,7 +358,7 @@ function exchange(amount, fromCurrency, toCurrency) {
345
358
  const exchangeRateModel = findExchangeRate(fromCurrency, toCurrency);
346
359
 
347
360
  if (exchangeRateModel) {
348
- return roundCurrency(applyExchangeStrategy(amount, exchangeRateModel));
361
+ return roundCurrency(applyExchangeStrategy(amount, exchangeRateModel));
349
362
  }
350
363
 
351
364
  // GET CURRENCY CONVERTER RATE WHEN NOT EXIST THE EXANGE RATE
@@ -356,7 +369,7 @@ function exchange(amount, fromCurrency, toCurrency) {
356
369
  }
357
370
 
358
371
  // Exchange With CURRENCY CONVERTER RATE
359
- return roundCurrency(amount * currencyConverterRateModel.rate);
372
+ return roundCurrency(amount * currencyConverterRateModel.rate);
360
373
  }
361
374
 
362
375
  // Get exchange rate
@@ -440,6 +453,7 @@ function calcPaymentAmount(payment, sale) {
440
453
  const total = calcTotal(sale);
441
454
  const payments = sale.payments || [];
442
455
 
456
+ // Find current payment index
443
457
  const index = payments.findIndex(p =>
444
458
  p.original_gived_amount === payment.original_gived_amount &&
445
459
  p.currency_id === payment.currency_id &&
@@ -450,6 +464,7 @@ function calcPaymentAmount(payment, sale) {
450
464
 
451
465
  let accumulated = 0;
452
466
 
467
+ // Calculate how much previous payments have covered
453
468
  for (let i = 0; i < index; i++) {
454
469
  const prevPayment = payments[i];
455
470
  const amount = exchange(
@@ -458,9 +473,10 @@ function calcPaymentAmount(payment, sale) {
458
473
  sale.currency_iso
459
474
  );
460
475
  const remaining = total - accumulated;
461
- accumulated += roundCurrency(Math.min(amount, remaining));
476
+ accumulated += Math.min(amount, remaining);
462
477
  }
463
478
 
479
+ // Calculate current payment amount that can be applied
464
480
  const currentAmount = exchange(
465
481
  payment.original_gived_amount,
466
482
  payment.original_currency_iso,
@@ -468,21 +484,25 @@ function calcPaymentAmount(payment, sale) {
468
484
  );
469
485
 
470
486
  const remaining = total - accumulated;
471
- return roundCurrency(Math.min(currentAmount, Math.max(remaining, 0)));
487
+ const appliedAmount = Math.min(currentAmount, Math.max(remaining, 0));
488
+
489
+ return roundCurrency(appliedAmount);
472
490
  }
473
491
 
474
492
  /**
475
- * : calcOriginalPaymentAmount()
493
+ * Calculates the original amount applied to sale (in payment's original currency)
476
494
  * @param {Object} payment
477
495
  * @param {Object} sale
478
496
  * @returns {number} Amount in payment's original currency
479
497
  */
480
498
  function calcOriginalPaymentAmount(payment, sale) {
481
499
  const effectiveAmount = calcPaymentAmount(payment, sale);
500
+
501
+ // Convert FROM sale currency TO payment's original currency
482
502
  return exchange(
483
503
  effectiveAmount,
484
- payment.original_currency_iso,
485
- sale.currency_iso
504
+ sale.currency_iso, // From sale currency
505
+ payment.original_currency_iso // To payment's original currency
486
506
  );
487
507
  }
488
508
 
@@ -520,20 +540,35 @@ function calcGivedPaymentAmount(payment, sale) {
520
540
  * - original_currency_iso: Payment currency ISO code
521
541
  * - exchange_rate: Calculated exchange rate between currencies
522
542
  */
523
- function calculatePaymentDetails(payment, enhancedSale) {
524
- // Calculate amounts
525
- const gived_amount = calcGivedPaymentAmount(payment, enhancedSale);
526
- const amount = calcPaymentAmount(payment, enhancedSale);
543
+ function calculatePaymentDetails(payment, sale) {
544
+ // Calculate the actual amount that can be applied to the sale (in sale currency)
545
+ const amount = calcPaymentAmount(payment, sale);
546
+
547
+ // Calculate the given amount in sale currency (full conversion without debt cap)
548
+ const gived_amount = exchange(
549
+ payment.original_gived_amount,
550
+ payment.original_currency_iso,
551
+ sale.currency_iso
552
+ );
553
+
554
+ // Calculate the original amount applied (in payment's original currency)
555
+ const original_amount = exchange(
556
+ amount, // This is in sale currency
557
+ sale.currency_iso,
558
+ payment.original_currency_iso
559
+ );
560
+
561
+ // Calculate exchange rate (sale currency per 1 unit of payment currency)
527
562
  const exchange_rate = gived_amount / payment.original_gived_amount;
528
- const original_amount = calcOriginalPaymentAmount(payment, enhancedSale);
529
563
 
530
564
  return {
531
565
  ...payment,
532
- amount,
533
- gived_amount,
534
- original_amount,
535
- original_gived_amount: payment.original_gived_amount,
566
+ amount, // Applied amount in sale currency
567
+ gived_amount, // Full given amount in sale currency
568
+ original_amount, // Applied amount in payment's original currency
569
+ original_gived_amount: payment.original_gived_amount, // Original given amount unchanged
536
570
  exchange_rate,
571
+ change_amount: Math.max(0, gived_amount - amount) // Change in sale currency
537
572
  };
538
573
  }
539
574
 
@@ -587,7 +622,7 @@ function appliedWholesaleLevels(sale) {
587
622
 
588
623
  function getSeparatedTaxes(sale) {
589
624
  const result = getApplicableDwTaxes(sale);
590
-
625
+
591
626
  return {
592
627
  sale_taxes: result.taxes.map(tax => ({
593
628
  tax_id: tax.id,
@@ -616,7 +651,7 @@ function getApplicableDwTaxes(sale) {
616
651
  validateSaleStructure(sale);
617
652
 
618
653
  const taxMap = {};
619
- const taxBatches = [];
654
+ const taxBatches = [];
620
655
 
621
656
  sale.dwSaleProducts.forEach((product, productIndex) => {
622
657
  const productSum = calcDwSaleProductSum(product, sale);
@@ -631,7 +666,7 @@ function getApplicableDwTaxes(sale) {
631
666
  allTaxes.forEach(tax => {
632
667
  const taxAmount = roundCurrency(netAmount * (tax.rate / 100));
633
668
  const taxKey = getTaxProductIndex(tax, tax.product_id);
634
-
669
+
635
670
  const taxBatch = {
636
671
  product_id: product.product_id,
637
672
  product_index: productIndex,
@@ -660,9 +695,9 @@ function getApplicableDwTaxes(sale) {
660
695
  });
661
696
 
662
697
  return Object.values(taxMap).map(tax => ({
663
- ...tax,
664
- amount: roundCurrency(tax.amount)
665
- })).filter(tax => tax.amount > 0);
698
+ ...tax,
699
+ amount: roundCurrency(tax.amount)
700
+ })).filter(tax => tax.amount > 0);
666
701
 
667
702
  /*
668
703
  return {
@@ -754,23 +789,23 @@ function calcTaxesByAbbreviation(sale, abbreviation) {
754
789
  */
755
790
  function getConsolidatedTaxes(sale) {
756
791
  validateSaleStructure(sale);
757
-
792
+
758
793
  const taxMap = {};
759
-
794
+
760
795
  sale.dwSaleProducts.forEach((product, productIndex) => {
761
796
  const productSum = calcDwSaleProductSum(product, sale);
762
797
  const discount = calcDwSaleProductDiscount(product, sale);
763
798
  const netAmount = roundCurrency(productSum - discount);
764
-
799
+
765
800
  // Get applicable taxes for this product
766
801
  const specificTaxes = getProductSpecificTaxes(product, sale);
767
802
  const generalTaxes = getGeneralTaxes(sale);
768
803
  const allTaxes = [...specificTaxes, ...generalTaxes];
769
-
804
+
770
805
  allTaxes.forEach(tax => {
771
806
  const taxAmount = roundCurrency(netAmount * (tax.rate / 100));
772
807
  const taxKey = `${tax.id}-${tax.abbreviation}-${tax.rate}`;
773
-
808
+
774
809
  if (taxMap[taxKey]) {
775
810
  // Merge with existing tax
776
811
  taxMap[taxKey].amount += taxAmount;
@@ -789,7 +824,7 @@ function getConsolidatedTaxes(sale) {
789
824
  }
790
825
  });
791
826
  });
792
-
827
+
793
828
  // Return unified taxes sorted by amount (descending)
794
829
  return Object.values(taxMap)
795
830
  .map(tax => ({
@@ -835,15 +870,15 @@ function roundCurrency(value, overrideRounding) {
835
870
  console.warn('roundCurrency: Invalid input', value);
836
871
  return 0;
837
872
  }
838
-
873
+
839
874
  // Handle zero and very small values
840
875
  if (Math.abs(value) < Number.EPSILON) {
841
876
  return 0;
842
877
  }
843
-
878
+
844
879
  const shouldRound = overrideRounding !== undefined ? overrideRounding : useRounding;
845
880
  let result;
846
-
881
+
847
882
  if (shouldRound) {
848
883
  // Rounding behavior
849
884
  result = Math.round(value * roundingFactor) / roundingFactor;
@@ -851,7 +886,7 @@ function roundCurrency(value, overrideRounding) {
851
886
  // Truncation behavior (default)
852
887
  result = Math.trunc(value * roundingFactor) / roundingFactor;
853
888
  }
854
-
889
+
855
890
  // Additional precision handling
856
891
  return parseFloat(result.toFixed(decimalPlaces));
857
892
  }
@@ -871,6 +906,7 @@ module.exports = {
871
906
  appliedWholesaleLevels,
872
907
  getApplicableDwTaxes,
873
908
  getConsolidatedTaxes,
909
+ calcDwSaleProductPrice,
874
910
  _internals: {
875
911
  calcDwSaleProductSum,
876
912
  calcDwSaleProductDiscount,
package/src/index.d.ts CHANGED
@@ -79,6 +79,8 @@ declare module 'softseti-sale-calculator-library' {
79
79
  */
80
80
  export function calculatePaymentDetails(payment: any, sale: Sale): PaymentCalculation;
81
81
 
82
+
83
+ export function calcDwSaleProductPrice(saleProduct: any, sale: any): number;
82
84
  /**
83
85
  * Gets applied wholesale levels
84
86
  * @param {Sale} sale - Sale object