shareneus 1.7.336 → 1.7.338
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/transaction-calculations/discounts-distribution.js +71 -73
- package/dist/transaction-calculations/transaction-calculation-engine.d.ts +1 -0
- package/dist/transaction-calculations/transaction-calculation-engine.js +66 -12
- package/package.json +1 -1
- package/src/transaction-calculations/discounts-distribution.ts +100 -111
- package/src/transaction-calculations/transaction-calculation-engine.ts +121 -45
|
@@ -3,9 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DiscountAndTaxCalculation = DiscountAndTaxCalculation;
|
|
4
4
|
exports.calculateRecordDiscountForLine = calculateRecordDiscountForLine;
|
|
5
5
|
const math_operations_1 = require("../shared/math-operations");
|
|
6
|
-
const util_1 = require("../shared/util");
|
|
7
|
-
const tax_calculator_1 = require("../tax/tax-calculator");
|
|
8
6
|
const tr_utils_1 = require("../utils/tr-utils");
|
|
7
|
+
const transaction_calculation_engine_1 = require("./transaction-calculation-engine");
|
|
9
8
|
function DiscountAndTaxCalculation(recordData, isTaxable, taxcodes = []) {
|
|
10
9
|
// ---------- STEP 0: FETCH TAX CODES ----------
|
|
11
10
|
// const taxcodes:any = isTaxable
|
|
@@ -79,7 +78,7 @@ function DiscountAndTaxCalculation(recordData, isTaxable, taxcodes = []) {
|
|
|
79
78
|
labor.RecDisc = totalTransactionDiscount;
|
|
80
79
|
const laborafterDiscPrice = (0, math_operations_1.Subtract)(labor.Amt, labor.Disc);
|
|
81
80
|
const afterDisc = (0, math_operations_1.Subtract)(laborafterDiscPrice, totalTransactionDiscount);
|
|
82
|
-
labor.Taxes = recalculateTaxes(labor,
|
|
81
|
+
labor.Taxes = recalculateTaxes(afterDisc, labor.TCode, taxcodes, labor.Taxes, labor.Qty || 1);
|
|
83
82
|
return labor;
|
|
84
83
|
});
|
|
85
84
|
// ---------- STEP 8: PROCESS INVENTORY ITEMS ----------
|
|
@@ -100,7 +99,7 @@ function DiscountAndTaxCalculation(recordData, isTaxable, taxcodes = []) {
|
|
|
100
99
|
item.RecDisc = totalTransactionDiscount;
|
|
101
100
|
const ItemafterDiscPrice = (0, math_operations_1.Subtract)(item.UnAmt, item.Disc);
|
|
102
101
|
const afterDisc = (0, math_operations_1.Subtract)(ItemafterDiscPrice, totalTransactionDiscount);
|
|
103
|
-
item.Taxes = recalculateTaxes(item,
|
|
102
|
+
item.Taxes = recalculateTaxes(afterDisc, item.TCode, taxcodes, item.Taxes, item.Qty);
|
|
104
103
|
return item;
|
|
105
104
|
});
|
|
106
105
|
// ---------- STEP 9: FINALIZE ----------
|
|
@@ -108,81 +107,80 @@ function DiscountAndTaxCalculation(recordData, isTaxable, taxcodes = []) {
|
|
|
108
107
|
recordData.Ops = [...finalServices, ...nonDiscountableOps];
|
|
109
108
|
return recordData;
|
|
110
109
|
}
|
|
111
|
-
function recalculateTaxes(
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
ComponentOverrides: line === null || line === void 0 ? void 0 : line.ComponentOverrides,
|
|
110
|
+
function recalculateTaxes(amount, taxCode, taxCodes = [], existingTaxes = [], qty = 1) {
|
|
111
|
+
const normalizedTaxCode = tr_utils_1.TrUtils.IsNull(taxCode) ? null : Number(taxCode);
|
|
112
|
+
const defaultRates = getDefaultTaxRates(taxCode, taxCodes);
|
|
113
|
+
const normalizedQty = toFiniteNumber(qty, 1);
|
|
114
|
+
return (Array.isArray(existingTaxes) ? existingTaxes : []).map((tax) => {
|
|
115
|
+
var _a, _b;
|
|
116
|
+
const code = tax === null || tax === void 0 ? void 0 : tax.Code;
|
|
117
|
+
const rate = Number(tax === null || tax === void 0 ? void 0 : tax.Rate);
|
|
118
|
+
const normalizedRate = Number.isFinite(rate) ? rate : (defaultRates[code] || 0);
|
|
119
|
+
const calcMethod = normalizeCalcMethod(tax === null || tax === void 0 ? void 0 : tax.CalcMethod);
|
|
120
|
+
return Object.assign(Object.assign({}, tax), { Rate: normalizedRate, Amt: calculateComponentTaxAmount(amount, normalizedRate, tax, normalizedQty), TaxCodeId: !tr_utils_1.TrUtils.IsNull(tax === null || tax === void 0 ? void 0 : tax.TaxCodeId)
|
|
121
|
+
? Number(tax.TaxCodeId)
|
|
122
|
+
: normalizedTaxCode, PerUnitAmt: (tax === null || tax === void 0 ? void 0 : tax.PerUnitAmt) || undefined, CalcMethod: calcMethod, Unit: (_b = (_a = tax === null || tax === void 0 ? void 0 : tax.Unit) !== null && _a !== void 0 ? _a : tax === null || tax === void 0 ? void 0 : tax.Units) !== null && _b !== void 0 ? _b : undefined });
|
|
125
123
|
});
|
|
126
|
-
const existingByCode = new Map(existingTaxes
|
|
127
|
-
.filter((tax) => tax === null || tax === void 0 ? void 0 : tax.Code)
|
|
128
|
-
.map((tax) => [tax.Code, tax]));
|
|
129
|
-
return calculatedTaxes.map((tax) => (Object.assign(Object.assign({}, (existingByCode.get(tax.Code) || {})), tax)));
|
|
130
124
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
125
|
+
function calculateComponentTaxAmount(amount, rate, tax, qty) {
|
|
126
|
+
const baseAmt = toFiniteNumber(amount);
|
|
127
|
+
const calcMethod = normalizeCalcMethod(tax === null || tax === void 0 ? void 0 : tax.CalcMethod);
|
|
128
|
+
const perUnitAmt = toFiniteNumber(tax === null || tax === void 0 ? void 0 : tax.PerUnitAmt);
|
|
129
|
+
const perUnitResult = (0, math_operations_1.Multiply)(perUnitAmt, qty);
|
|
130
|
+
const percentResult = (0, math_operations_1.Multiply)(baseAmt, (0, math_operations_1.Divide)(rate, 100));
|
|
131
|
+
switch (calcMethod) {
|
|
132
|
+
case 'PerUnit':
|
|
133
|
+
return perUnitResult;
|
|
134
|
+
case 'PerUnitPlusPercent':
|
|
135
|
+
return (0, math_operations_1.Add)(perUnitResult, percentResult);
|
|
136
|
+
case 'MaxOfPercentOrPerUnit':
|
|
137
|
+
return Math.max(perUnitResult, percentResult);
|
|
138
|
+
case 'Percent':
|
|
139
|
+
default:
|
|
140
|
+
return percentResult;
|
|
141
141
|
}
|
|
142
|
-
return taxCodes.find((code) => String(code === null || code === void 0 ? void 0 : code._id) === String(taxCodeId)) || null;
|
|
143
142
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return Object.assign(Object.assign({}, taxCode), { Components: buildLegacyComponents(taxCode, existingTaxes) });
|
|
143
|
+
function normalizeCalcMethod(calcMethod) {
|
|
144
|
+
return calcMethod === 'Percentage' || tr_utils_1.TrUtils.IsNull(calcMethod)
|
|
145
|
+
? 'Percent'
|
|
146
|
+
: calcMethod;
|
|
149
147
|
}
|
|
150
|
-
function
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
Code: code,
|
|
154
|
-
Name: code,
|
|
155
|
-
Rate: (0, util_1.GetNumber)(taxCode === null || taxCode === void 0 ? void 0 : taxCode[code]),
|
|
156
|
-
Type: "Tax",
|
|
157
|
-
CalcMethod: "Percent",
|
|
158
|
-
AppliedOn: "NetAmt",
|
|
159
|
-
}))
|
|
160
|
-
.filter((component) => component.Rate > 0);
|
|
161
|
-
existingTaxes.forEach((tax) => {
|
|
162
|
-
if (!(tax === null || tax === void 0 ? void 0 : tax.Code) || components.some((component) => component.Code === tax.Code)) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
components.push({
|
|
166
|
-
Code: tax.Code,
|
|
167
|
-
Name: tax.Name || tax.Code,
|
|
168
|
-
Rate: (0, util_1.GetNumber)(tax.Rate),
|
|
169
|
-
Type: tax.Type || (String(tax.Code).toUpperCase().includes("CESS") ? "Cess" : "Tax"),
|
|
170
|
-
CalcMethod: tax.CalcMethod || "Percent",
|
|
171
|
-
PerUnitAmt: tax.PerUnitAmt,
|
|
172
|
-
Unit: tax.Unit,
|
|
173
|
-
AppliedOn: tax.AppliedOn || "NetAmt",
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
return components;
|
|
148
|
+
function toFiniteNumber(value, fallback = 0) {
|
|
149
|
+
const normalized = Number(value);
|
|
150
|
+
return Number.isFinite(normalized) ? normalized : fallback;
|
|
177
151
|
}
|
|
178
|
-
function
|
|
179
|
-
const normalizedTaxCode = tr_utils_1.TrUtils.IsNull(taxCode) ? null :
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
152
|
+
function getDefaultTaxRates(taxCode, taxCodes = []) {
|
|
153
|
+
const normalizedTaxCode = tr_utils_1.TrUtils.IsNull(taxCode) ? null : taxCode;
|
|
154
|
+
const taxCodeRow = Array.isArray(taxCodes)
|
|
155
|
+
? taxCodes.find((code) => (code === null || code === void 0 ? void 0 : code._id) === normalizedTaxCode)
|
|
156
|
+
: null;
|
|
157
|
+
const components = Array.isArray(taxCodeRow === null || taxCodeRow === void 0 ? void 0 : taxCodeRow.Components)
|
|
158
|
+
? taxCodeRow.Components
|
|
159
|
+
: [];
|
|
160
|
+
const getComponentRate = (code) => {
|
|
161
|
+
const match = components.find((component) => {
|
|
162
|
+
var _a, _b;
|
|
163
|
+
const compCode = (_b = (_a = component === null || component === void 0 ? void 0 : component.Code) !== null && _a !== void 0 ? _a : component === null || component === void 0 ? void 0 : component.Type) !== null && _b !== void 0 ? _b : component === null || component === void 0 ? void 0 : component.Name;
|
|
164
|
+
return compCode === code;
|
|
165
|
+
});
|
|
166
|
+
const rate = Number(match === null || match === void 0 ? void 0 : match.Rate);
|
|
167
|
+
return Number.isFinite(rate) ? rate : 0;
|
|
168
|
+
};
|
|
169
|
+
let CGST = getComponentRate('CGST');
|
|
170
|
+
let SGST = getComponentRate('SGST');
|
|
171
|
+
let IGST = getComponentRate('IGST');
|
|
172
|
+
const CESS = getComponentRate('CESS');
|
|
173
|
+
if (CGST === 0 && SGST === 0 && IGST === 0) {
|
|
174
|
+
const gstRateSum = transaction_calculation_engine_1.TransactionCalculationEngine.getGSTRateSumByTaxCode(normalizedTaxCode, taxCodes);
|
|
175
|
+
if (components.length === 1) {
|
|
176
|
+
IGST = gstRateSum;
|
|
177
|
+
}
|
|
178
|
+
else if (components.length === 2) {
|
|
179
|
+
CGST = gstRateSum / 2;
|
|
180
|
+
SGST = gstRateSum / 2;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { CGST, SGST, IGST, CESS };
|
|
186
184
|
}
|
|
187
185
|
/**
|
|
188
186
|
* Calculates the record-level discount amount (RecDisc) for a single line.
|
|
@@ -48,6 +48,59 @@ function sumTaxRateFromTaxes(taxes) {
|
|
|
48
48
|
const rows = getTaxesArray(taxes);
|
|
49
49
|
return rows.reduce((sum, row) => { var _a; return (0, math_operations_1.Add)(sum, toNum((_a = row === null || row === void 0 ? void 0 : row.Rate) !== null && _a !== void 0 ? _a : row === null || row === void 0 ? void 0 : row.rate)); }, 0);
|
|
50
50
|
}
|
|
51
|
+
function normalizeCalcMethod(calcMethod) {
|
|
52
|
+
return calcMethod === 'Percentage' || isNullOrEmpty(calcMethod)
|
|
53
|
+
? 'Percent'
|
|
54
|
+
: String(calcMethod);
|
|
55
|
+
}
|
|
56
|
+
function getTaxRowQty(row, fallbackQty) {
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
return toNum((_c = (_b = (_a = row === null || row === void 0 ? void 0 : row.Qty) !== null && _a !== void 0 ? _a : row === null || row === void 0 ? void 0 : row.qty) !== null && _b !== void 0 ? _b : row === null || row === void 0 ? void 0 : row.Quantity) !== null && _c !== void 0 ? _c : row === null || row === void 0 ? void 0 : row.quantity) || fallbackQty;
|
|
59
|
+
}
|
|
60
|
+
function getPerUnitTaxAmount(row, fallbackQty, fallbackToAmt = true) {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
const perUnitAmt = toNum((_a = row === null || row === void 0 ? void 0 : row.PerUnitAmt) !== null && _a !== void 0 ? _a : row === null || row === void 0 ? void 0 : row.perUnitAmt);
|
|
63
|
+
const qty = getTaxRowQty(row, fallbackQty);
|
|
64
|
+
if (perUnitAmt !== 0 && qty !== 0) {
|
|
65
|
+
return (0, math_operations_1.Multiply)(perUnitAmt, qty);
|
|
66
|
+
}
|
|
67
|
+
return fallbackToAmt ? toNum((_b = row === null || row === void 0 ? void 0 : row.Amt) !== null && _b !== void 0 ? _b : row === null || row === void 0 ? void 0 : row.amt) : 0;
|
|
68
|
+
}
|
|
69
|
+
function getReverseTaxBreakdown(taxes, qty) {
|
|
70
|
+
return getTaxesArray(taxes).reduce((summary, row) => {
|
|
71
|
+
var _a, _b, _c;
|
|
72
|
+
const calcMethod = normalizeCalcMethod((_a = row === null || row === void 0 ? void 0 : row.CalcMethod) !== null && _a !== void 0 ? _a : row === null || row === void 0 ? void 0 : row.calcMethod);
|
|
73
|
+
const rate = toNum((_b = row === null || row === void 0 ? void 0 : row.Rate) !== null && _b !== void 0 ? _b : row === null || row === void 0 ? void 0 : row.rate);
|
|
74
|
+
switch (calcMethod) {
|
|
75
|
+
case 'PerUnit':
|
|
76
|
+
summary.fixedTaxAmount = (0, math_operations_1.Add)(summary.fixedTaxAmount, getPerUnitTaxAmount(row, qty));
|
|
77
|
+
break;
|
|
78
|
+
case 'PerUnitPlusPercent':
|
|
79
|
+
summary.fixedTaxAmount = (0, math_operations_1.Add)(summary.fixedTaxAmount, getPerUnitTaxAmount(row, qty, false));
|
|
80
|
+
summary.percentTaxRate = (0, math_operations_1.Add)(summary.percentTaxRate, rate);
|
|
81
|
+
break;
|
|
82
|
+
case 'MaxOfPercentOrPerUnit': {
|
|
83
|
+
const amt = toNum((_c = row === null || row === void 0 ? void 0 : row.Amt) !== null && _c !== void 0 ? _c : row === null || row === void 0 ? void 0 : row.amt);
|
|
84
|
+
const perUnitAmt = getPerUnitTaxAmount(row, qty);
|
|
85
|
+
if (amt !== 0) {
|
|
86
|
+
summary.fixedTaxAmount = (0, math_operations_1.Add)(summary.fixedTaxAmount, amt);
|
|
87
|
+
}
|
|
88
|
+
else if (perUnitAmt !== 0) {
|
|
89
|
+
summary.fixedTaxAmount = (0, math_operations_1.Add)(summary.fixedTaxAmount, perUnitAmt);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
summary.percentTaxRate = (0, math_operations_1.Add)(summary.percentTaxRate, rate);
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'Percent':
|
|
97
|
+
default:
|
|
98
|
+
summary.percentTaxRate = (0, math_operations_1.Add)(summary.percentTaxRate, rate);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
return summary;
|
|
102
|
+
}, { fixedTaxAmount: 0, percentTaxRate: 0 });
|
|
103
|
+
}
|
|
51
104
|
/**
|
|
52
105
|
* Distribute a flat discount amount across a set of rows proportionally
|
|
53
106
|
* by each row's `baseKey` value. Returns an array of share amounts aligned
|
|
@@ -115,12 +168,13 @@ class TransactionCalculationEngine {
|
|
|
115
168
|
if (!input.includeTaxInTotal) {
|
|
116
169
|
return total;
|
|
117
170
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
171
|
+
const taxBreakdown = getReverseTaxBreakdown(input.taxRows, toNum(input.qty) || 1);
|
|
172
|
+
const totalAfterFixedTax = (0, math_operations_1.Subtract)(total, taxBreakdown.fixedTaxAmount);
|
|
173
|
+
if (taxBreakdown.percentTaxRate) {
|
|
174
|
+
const divisor = (0, math_operations_1.Add)(1, (0, math_operations_1.Divide)(taxBreakdown.percentTaxRate, 100));
|
|
175
|
+
return divisor === 0 ? totalAfterFixedTax : (0, math_operations_1.Divide)(totalAfterFixedTax, divisor);
|
|
122
176
|
}
|
|
123
|
-
return
|
|
177
|
+
return totalAfterFixedTax;
|
|
124
178
|
}
|
|
125
179
|
static reverseCalculateAmountAndUnitPriceFromTotal(input) {
|
|
126
180
|
const qty = toNum(input.qty) || 1;
|
|
@@ -128,6 +182,7 @@ class TransactionCalculationEngine {
|
|
|
128
182
|
total: input.total,
|
|
129
183
|
taxRows: input.taxRows,
|
|
130
184
|
includeTaxInTotal: input.includeTaxInTotal,
|
|
185
|
+
qty,
|
|
131
186
|
});
|
|
132
187
|
const disc = toNum(input.disc);
|
|
133
188
|
const recDisc = toNum(input.recDisc);
|
|
@@ -369,15 +424,14 @@ class TransactionCalculationEngine {
|
|
|
369
424
|
if (qty === 0) {
|
|
370
425
|
return Object.assign({}, item);
|
|
371
426
|
}
|
|
372
|
-
const taxSummary = TransactionCalculationEngine.getTaxesSummary((_a = item === null || item === void 0 ? void 0 : item.Taxes) !== null && _a !== void 0 ? _a : item === null || item === void 0 ? void 0 : item.taxes);
|
|
373
427
|
// If total doesn't include tax, treat it as "already stripped".
|
|
374
428
|
const subtotal = includeTaxInTotal
|
|
375
|
-
?
|
|
376
|
-
|
|
377
|
-
: (
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
429
|
+
? TransactionCalculationEngine.calculateSubtotalFromTotalUsingTaxes({
|
|
430
|
+
total,
|
|
431
|
+
taxRows: (_a = item === null || item === void 0 ? void 0 : item.Taxes) !== null && _a !== void 0 ? _a : item === null || item === void 0 ? void 0 : item.taxes,
|
|
432
|
+
includeTaxInTotal,
|
|
433
|
+
qty,
|
|
434
|
+
})
|
|
381
435
|
: total;
|
|
382
436
|
if (item.NoDisc) {
|
|
383
437
|
const unpr = (0, math_operations_1.Divide)(subtotal, qty);
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Add, Divide, Multiply, Subtract } from "../shared/math-operations";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { TrUtils } from "../utils/tr-utils";
|
|
1
|
+
import { Add, Divide, Multiply, Subtract } from "../shared/math-operations";
|
|
2
|
+
import { TrUtils } from "../utils/tr-utils";
|
|
3
|
+
import { TransactionCalculationEngine } from "./transaction-calculation-engine";
|
|
5
4
|
|
|
6
5
|
export function DiscountAndTaxCalculation(recordData: any, isTaxable: boolean, taxcodes: any = []) {
|
|
7
6
|
|
|
@@ -102,7 +101,7 @@ export function DiscountAndTaxCalculation(recordData: any, isTaxable: boolean, t
|
|
|
102
101
|
const laborafterDiscPrice = Subtract(labor.Amt, labor.Disc);
|
|
103
102
|
const afterDisc = Subtract(laborafterDiscPrice, totalTransactionDiscount);
|
|
104
103
|
|
|
105
|
-
labor.Taxes = recalculateTaxes(labor,
|
|
104
|
+
labor.Taxes = recalculateTaxes(afterDisc, labor.TCode, taxcodes, labor.Taxes, labor.Qty || 1);
|
|
106
105
|
|
|
107
106
|
return labor;
|
|
108
107
|
});
|
|
@@ -135,7 +134,7 @@ export function DiscountAndTaxCalculation(recordData: any, isTaxable: boolean, t
|
|
|
135
134
|
const ItemafterDiscPrice = Subtract(item.UnAmt, item.Disc);
|
|
136
135
|
const afterDisc = Subtract(ItemafterDiscPrice, totalTransactionDiscount);
|
|
137
136
|
|
|
138
|
-
item.Taxes = recalculateTaxes(item,
|
|
137
|
+
item.Taxes = recalculateTaxes(afterDisc, item.TCode, taxcodes, item.Taxes, item.Qty);
|
|
139
138
|
|
|
140
139
|
return item;
|
|
141
140
|
});
|
|
@@ -147,111 +146,101 @@ export function DiscountAndTaxCalculation(recordData: any, isTaxable: boolean, t
|
|
|
147
146
|
return recordData;
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
function recalculateTaxes(
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
...tax,
|
|
247
|
-
Rate: rate,
|
|
248
|
-
Amt: Multiply(amount, Divide(rate, 100)),
|
|
249
|
-
TaxCodeId: !TrUtils.IsNull(tax?.TaxCodeId)
|
|
250
|
-
? Number(tax.TaxCodeId)
|
|
251
|
-
: normalizedTaxCode
|
|
252
|
-
};
|
|
253
|
-
});
|
|
254
|
-
}
|
|
149
|
+
function recalculateTaxes(amount: any, taxCode: any, taxCodes: any[] = [], existingTaxes: any[] = [], qty: any = 1): any[] {
|
|
150
|
+
const normalizedTaxCode = TrUtils.IsNull(taxCode) ? null : Number(taxCode);
|
|
151
|
+
const defaultRates = getDefaultTaxRates(taxCode, taxCodes);
|
|
152
|
+
const normalizedQty = toFiniteNumber(qty, 1);
|
|
153
|
+
|
|
154
|
+
return (Array.isArray(existingTaxes) ? existingTaxes : []).map((tax: any) => {
|
|
155
|
+
const code = tax?.Code;
|
|
156
|
+
const rate = Number(tax?.Rate);
|
|
157
|
+
const normalizedRate = Number.isFinite(rate) ? rate : (defaultRates[code] || 0);
|
|
158
|
+
const calcMethod = normalizeCalcMethod(tax?.CalcMethod);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...tax,
|
|
162
|
+
Rate: normalizedRate,
|
|
163
|
+
Amt: calculateComponentTaxAmount(amount, normalizedRate, tax, normalizedQty),
|
|
164
|
+
TaxCodeId: !TrUtils.IsNull(tax?.TaxCodeId)
|
|
165
|
+
? Number(tax.TaxCodeId)
|
|
166
|
+
: normalizedTaxCode,
|
|
167
|
+
PerUnitAmt: tax?.PerUnitAmt || undefined,
|
|
168
|
+
CalcMethod: calcMethod,
|
|
169
|
+
Unit: tax?.Unit ?? tax?.Units ?? undefined,
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function calculateComponentTaxAmount(amount: any, rate: number, tax: any, qty: number): number {
|
|
175
|
+
const baseAmt = toFiniteNumber(amount);
|
|
176
|
+
const calcMethod = normalizeCalcMethod(tax?.CalcMethod);
|
|
177
|
+
const perUnitAmt = toFiniteNumber(tax?.PerUnitAmt);
|
|
178
|
+
const perUnitResult = Multiply(perUnitAmt, qty);
|
|
179
|
+
const percentResult = Multiply(baseAmt, Divide(rate, 100));
|
|
180
|
+
|
|
181
|
+
switch (calcMethod) {
|
|
182
|
+
case 'PerUnit':
|
|
183
|
+
return perUnitResult;
|
|
184
|
+
case 'PerUnitPlusPercent':
|
|
185
|
+
return Add(perUnitResult, percentResult);
|
|
186
|
+
case 'MaxOfPercentOrPerUnit':
|
|
187
|
+
return Math.max(perUnitResult, percentResult);
|
|
188
|
+
case 'Percent':
|
|
189
|
+
default:
|
|
190
|
+
return percentResult;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function normalizeCalcMethod(calcMethod: any): string {
|
|
195
|
+
return calcMethod === 'Percentage' || TrUtils.IsNull(calcMethod)
|
|
196
|
+
? 'Percent'
|
|
197
|
+
: calcMethod;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function toFiniteNumber(value: any, fallback = 0): number {
|
|
201
|
+
const normalized = Number(value);
|
|
202
|
+
return Number.isFinite(normalized) ? normalized : fallback;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getDefaultTaxRates(taxCode: any, taxCodes: any[] = []): any {
|
|
206
|
+
const normalizedTaxCode = TrUtils.IsNull(taxCode) ? null : taxCode;
|
|
207
|
+
const taxCodeRow = Array.isArray(taxCodes)
|
|
208
|
+
? taxCodes.find((code: any) => code?._id === normalizedTaxCode)
|
|
209
|
+
: null;
|
|
210
|
+
const components = Array.isArray(taxCodeRow?.Components)
|
|
211
|
+
? taxCodeRow.Components
|
|
212
|
+
: [];
|
|
213
|
+
|
|
214
|
+
const getComponentRate = (code: string): number => {
|
|
215
|
+
const match = components.find((component: any) => {
|
|
216
|
+
const compCode = component?.Code ?? component?.Type ?? component?.Name;
|
|
217
|
+
return compCode === code;
|
|
218
|
+
});
|
|
219
|
+
const rate = Number(match?.Rate);
|
|
220
|
+
return Number.isFinite(rate) ? rate : 0;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
let CGST = getComponentRate('CGST');
|
|
224
|
+
let SGST = getComponentRate('SGST');
|
|
225
|
+
let IGST = getComponentRate('IGST');
|
|
226
|
+
const CESS = getComponentRate('CESS');
|
|
227
|
+
|
|
228
|
+
if (CGST === 0 && SGST === 0 && IGST === 0) {
|
|
229
|
+
const gstRateSum =
|
|
230
|
+
TransactionCalculationEngine.getGSTRateSumByTaxCode(
|
|
231
|
+
normalizedTaxCode,
|
|
232
|
+
taxCodes,
|
|
233
|
+
);
|
|
234
|
+
if (components.length === 1) {
|
|
235
|
+
IGST = gstRateSum;
|
|
236
|
+
} else if (components.length === 2) {
|
|
237
|
+
CGST = gstRateSum / 2;
|
|
238
|
+
SGST = gstRateSum / 2;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return { CGST, SGST, IGST, CESS };
|
|
243
|
+
}
|
|
255
244
|
|
|
256
245
|
/**
|
|
257
246
|
* Calculates the record-level discount amount (RecDisc) for a single line.
|
|
@@ -65,13 +65,88 @@ function sumTaxAmountFromTaxes(taxes: any): number {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
function sumTaxRateFromTaxes(taxes: any): number {
|
|
69
|
-
const rows = getTaxesArray(taxes);
|
|
70
|
-
return rows.reduce(
|
|
71
|
-
(sum, row) => Add(sum, toNum(row?.Rate ?? row?.rate)),
|
|
72
|
-
0,
|
|
73
|
-
);
|
|
74
|
-
}
|
|
68
|
+
function sumTaxRateFromTaxes(taxes: any): number {
|
|
69
|
+
const rows = getTaxesArray(taxes);
|
|
70
|
+
return rows.reduce(
|
|
71
|
+
(sum, row) => Add(sum, toNum(row?.Rate ?? row?.rate)),
|
|
72
|
+
0,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeCalcMethod(calcMethod: any): string {
|
|
77
|
+
return calcMethod === 'Percentage' || isNullOrEmpty(calcMethod)
|
|
78
|
+
? 'Percent'
|
|
79
|
+
: String(calcMethod);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getTaxRowQty(row: any, fallbackQty: number): number {
|
|
83
|
+
return toNum(row?.Qty ?? row?.qty ?? row?.Quantity ?? row?.quantity) || fallbackQty;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getPerUnitTaxAmount(
|
|
87
|
+
row: any,
|
|
88
|
+
fallbackQty: number,
|
|
89
|
+
fallbackToAmt = true,
|
|
90
|
+
): number {
|
|
91
|
+
const perUnitAmt = toNum(row?.PerUnitAmt ?? row?.perUnitAmt);
|
|
92
|
+
const qty = getTaxRowQty(row, fallbackQty);
|
|
93
|
+
|
|
94
|
+
if (perUnitAmt !== 0 && qty !== 0) {
|
|
95
|
+
return Multiply(perUnitAmt, qty);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return fallbackToAmt ? toNum(row?.Amt ?? row?.amt) : 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getReverseTaxBreakdown(taxes: any, qty: number): {
|
|
102
|
+
fixedTaxAmount: number;
|
|
103
|
+
percentTaxRate: number;
|
|
104
|
+
} {
|
|
105
|
+
return getTaxesArray(taxes).reduce(
|
|
106
|
+
(summary, row) => {
|
|
107
|
+
const calcMethod = normalizeCalcMethod(row?.CalcMethod ?? row?.calcMethod);
|
|
108
|
+
const rate = toNum(row?.Rate ?? row?.rate);
|
|
109
|
+
|
|
110
|
+
switch (calcMethod) {
|
|
111
|
+
case 'PerUnit':
|
|
112
|
+
summary.fixedTaxAmount = Add(
|
|
113
|
+
summary.fixedTaxAmount,
|
|
114
|
+
getPerUnitTaxAmount(row, qty),
|
|
115
|
+
);
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
case 'PerUnitPlusPercent':
|
|
119
|
+
summary.fixedTaxAmount = Add(
|
|
120
|
+
summary.fixedTaxAmount,
|
|
121
|
+
getPerUnitTaxAmount(row, qty, false),
|
|
122
|
+
);
|
|
123
|
+
summary.percentTaxRate = Add(summary.percentTaxRate, rate);
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case 'MaxOfPercentOrPerUnit': {
|
|
127
|
+
const amt = toNum(row?.Amt ?? row?.amt);
|
|
128
|
+
const perUnitAmt = getPerUnitTaxAmount(row, qty);
|
|
129
|
+
if (amt !== 0) {
|
|
130
|
+
summary.fixedTaxAmount = Add(summary.fixedTaxAmount, amt);
|
|
131
|
+
} else if (perUnitAmt !== 0) {
|
|
132
|
+
summary.fixedTaxAmount = Add(summary.fixedTaxAmount, perUnitAmt);
|
|
133
|
+
} else {
|
|
134
|
+
summary.percentTaxRate = Add(summary.percentTaxRate, rate);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case 'Percent':
|
|
140
|
+
default:
|
|
141
|
+
summary.percentTaxRate = Add(summary.percentTaxRate, rate);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return summary;
|
|
146
|
+
},
|
|
147
|
+
{ fixedTaxAmount: 0, percentTaxRate: 0 },
|
|
148
|
+
);
|
|
149
|
+
}
|
|
75
150
|
|
|
76
151
|
/**
|
|
77
152
|
* Distribute a flat discount amount across a set of rows proportionally
|
|
@@ -153,26 +228,30 @@ export class TransactionCalculationEngine {
|
|
|
153
228
|
: baseAmount;
|
|
154
229
|
}
|
|
155
230
|
|
|
156
|
-
static calculateSubtotalFromTotalUsingTaxes(input: {
|
|
157
|
-
total: number;
|
|
158
|
-
taxRows: unknown;
|
|
159
|
-
includeTaxInTotal: boolean;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
231
|
+
static calculateSubtotalFromTotalUsingTaxes(input: {
|
|
232
|
+
total: number;
|
|
233
|
+
taxRows: unknown;
|
|
234
|
+
includeTaxInTotal: boolean;
|
|
235
|
+
qty?: number;
|
|
236
|
+
}): number {
|
|
237
|
+
const total = toNum(input.total);
|
|
238
|
+
if (!input.includeTaxInTotal) {
|
|
239
|
+
return total;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const taxBreakdown = getReverseTaxBreakdown(
|
|
243
|
+
input.taxRows,
|
|
244
|
+
toNum(input.qty) || 1,
|
|
245
|
+
);
|
|
246
|
+
const totalAfterFixedTax = Subtract(total, taxBreakdown.fixedTaxAmount);
|
|
247
|
+
|
|
248
|
+
if (taxBreakdown.percentTaxRate) {
|
|
249
|
+
const divisor = Add(1, Divide(taxBreakdown.percentTaxRate, 100));
|
|
250
|
+
return divisor === 0 ? totalAfterFixedTax : Divide(totalAfterFixedTax, divisor);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return totalAfterFixedTax;
|
|
254
|
+
}
|
|
176
255
|
|
|
177
256
|
static reverseCalculateAmountAndUnitPriceFromTotal(input: {
|
|
178
257
|
qty: number;
|
|
@@ -185,11 +264,12 @@ export class TransactionCalculationEngine {
|
|
|
185
264
|
}): { amount: number; unitPrice: number } {
|
|
186
265
|
const qty = toNum(input.qty) || 1;
|
|
187
266
|
const subtotal =
|
|
188
|
-
TransactionCalculationEngine.calculateSubtotalFromTotalUsingTaxes({
|
|
189
|
-
total: input.total,
|
|
190
|
-
taxRows: input.taxRows,
|
|
191
|
-
includeTaxInTotal: input.includeTaxInTotal,
|
|
192
|
-
|
|
267
|
+
TransactionCalculationEngine.calculateSubtotalFromTotalUsingTaxes({
|
|
268
|
+
total: input.total,
|
|
269
|
+
taxRows: input.taxRows,
|
|
270
|
+
includeTaxInTotal: input.includeTaxInTotal,
|
|
271
|
+
qty,
|
|
272
|
+
});
|
|
193
273
|
|
|
194
274
|
const disc = toNum(input.disc);
|
|
195
275
|
const recDisc = toNum(input.recDisc);
|
|
@@ -474,19 +554,15 @@ export class TransactionCalculationEngine {
|
|
|
474
554
|
return { ...item };
|
|
475
555
|
}
|
|
476
556
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const divisor = Add(1, Divide(taxSummary.taxRate, 100));
|
|
487
|
-
return Divide(total, divisor === 0 ? 1 : divisor);
|
|
488
|
-
})()
|
|
489
|
-
: total;
|
|
557
|
+
// If total doesn't include tax, treat it as "already stripped".
|
|
558
|
+
const subtotal = includeTaxInTotal
|
|
559
|
+
? TransactionCalculationEngine.calculateSubtotalFromTotalUsingTaxes({
|
|
560
|
+
total,
|
|
561
|
+
taxRows: item?.Taxes ?? item?.taxes,
|
|
562
|
+
includeTaxInTotal,
|
|
563
|
+
qty,
|
|
564
|
+
})
|
|
565
|
+
: total;
|
|
490
566
|
|
|
491
567
|
if (item.NoDisc) {
|
|
492
568
|
const unpr = Divide(subtotal, qty);
|