@qrwise/pos-calculations 0.0.19 → 0.0.21
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/index.cjs +29 -11
- package/dist/index.d.cts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +29 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,11 @@ var splitDiscountTypes = [
|
|
|
69
69
|
{ type: "MOV", count: "movCount", breakdown: "mov", vatExempt: false },
|
|
70
70
|
{ type: "SOLO", count: "soloParentCount", breakdown: "solo", vatExempt: false }
|
|
71
71
|
];
|
|
72
|
+
function resolvedVatDivisor(input) {
|
|
73
|
+
if (input.isVatRegistered === false) return 1;
|
|
74
|
+
const divisor = Number(input.vatRate || 0) / 100;
|
|
75
|
+
return divisor > 0 ? divisor : 1;
|
|
76
|
+
}
|
|
72
77
|
function normalizedCount(value) {
|
|
73
78
|
return Math.max(0, Math.floor(toMoneyNumber(value)));
|
|
74
79
|
}
|
|
@@ -79,7 +84,7 @@ function calculateSplitTotals(input) {
|
|
|
79
84
|
const concessionCount = splitDiscountTypes.reduce((total, item) => total + normalizedCount(counts[item.count]), 0);
|
|
80
85
|
const paxCount = regularCount + concessionCount;
|
|
81
86
|
if (paxCount === 0) return null;
|
|
82
|
-
const vatDivisor =
|
|
87
|
+
const vatDivisor = resolvedVatDivisor(input);
|
|
83
88
|
const vatRate = Math.max(0, vatDivisor - 1);
|
|
84
89
|
const scRate = Number(input.serviceChargeRate || 0) / 100;
|
|
85
90
|
const discountableAmount = Math.max(0, toMoneyNumber(input.discountableTotalAmount));
|
|
@@ -109,6 +114,10 @@ function calculateSplitTotals(input) {
|
|
|
109
114
|
discountBreakdown[item.breakdown] = -discount;
|
|
110
115
|
totalDiscount += discount;
|
|
111
116
|
}
|
|
117
|
+
if (input.isVatRegistered === false) {
|
|
118
|
+
vatExemptSales += vatSales;
|
|
119
|
+
vatSales = 0;
|
|
120
|
+
}
|
|
112
121
|
vatSales = format2(vatSales);
|
|
113
122
|
vatExemptSales = format2(vatExemptSales);
|
|
114
123
|
const discounted = -format2(totalDiscount);
|
|
@@ -123,7 +132,7 @@ function calculateSplitTotals(input) {
|
|
|
123
132
|
return {
|
|
124
133
|
quantity: input.quantity,
|
|
125
134
|
subtotal,
|
|
126
|
-
isAddVat: splitDiscountTypes.some((item) => !item.vatExempt && normalizedCount(counts[item.count]) > 0),
|
|
135
|
+
isAddVat: input.isVatRegistered !== false && splitDiscountTypes.some((item) => !item.vatExempt && normalizedCount(counts[item.count]) > 0),
|
|
127
136
|
vatNet,
|
|
128
137
|
vat,
|
|
129
138
|
discounted,
|
|
@@ -170,7 +179,8 @@ function calculateSplitTotals(input) {
|
|
|
170
179
|
function calculateTotals(input) {
|
|
171
180
|
const splitTotals = calculateSplitTotals(input);
|
|
172
181
|
if (splitTotals) return splitTotals;
|
|
173
|
-
const vatRate =
|
|
182
|
+
const vatRate = resolvedVatDivisor(input);
|
|
183
|
+
const vatPercent = Math.max(0, vatRate - 1);
|
|
174
184
|
const scRate = Number(input.serviceChargeRate || 0) / 100;
|
|
175
185
|
const discountRate = Number(input.discountRate || 0) / 100;
|
|
176
186
|
const voucherRate = input.voucherType === "FIXED" ? input.voucherRate : Number(input.voucherRate || 0) / 100;
|
|
@@ -185,11 +195,11 @@ function calculateTotals(input) {
|
|
|
185
195
|
}
|
|
186
196
|
const subtotal = totalOrderAmount + voucherDiscounted;
|
|
187
197
|
const voucherVatNet2 = format2(totalOrderAmount / vatRate);
|
|
188
|
-
const voucherVat = format2(voucherVatNet2 *
|
|
198
|
+
const voucherVat = format2(voucherVatNet2 * vatPercent);
|
|
189
199
|
let vatNet = format2(subtotal / vatRate);
|
|
190
|
-
let vat = format2(vatNet *
|
|
200
|
+
let vat = format2(vatNet * vatPercent);
|
|
191
201
|
const isSpecial = discountRate > 0;
|
|
192
|
-
const isAddVat = isAddVatAfterDiscount(input.discountType);
|
|
202
|
+
const isAddVat = input.isVatRegistered === false ? false : isAddVatAfterDiscount(input.discountType);
|
|
193
203
|
const discounted = format2(discountRate * -vatNet);
|
|
194
204
|
if (isSpecial) {
|
|
195
205
|
vatNet = format2(vatNet + discounted);
|
|
@@ -406,6 +416,17 @@ function voucherVatNet(transaction, voucher, discount) {
|
|
|
406
416
|
const eligibleGross = itemizedGross > 0 ? itemizedGross : percentageGross > 0 ? percentageGross : gross;
|
|
407
417
|
return format2(Math.min(gross, eligibleGross) / 1.12);
|
|
408
418
|
}
|
|
419
|
+
function itemizedVoucherCount(transaction) {
|
|
420
|
+
const splits = transaction.metadata?.split_discounts;
|
|
421
|
+
if (!Array.isArray(splits)) return 0;
|
|
422
|
+
return splits.reduce((count, rawSplit) => {
|
|
423
|
+
if (!rawSplit || typeof rawSplit !== "object") return count;
|
|
424
|
+
const rawVoucher = rawSplit.voucher;
|
|
425
|
+
if (!rawVoucher || typeof rawVoucher !== "object") return count;
|
|
426
|
+
const voucher = rawVoucher;
|
|
427
|
+
return Math.abs(toMoneyNumber(voucher.discount_amount)) > 0 ? count + 1 : count;
|
|
428
|
+
}, 0);
|
|
429
|
+
}
|
|
409
430
|
function buildTransactionProjection(transaction, options = {}) {
|
|
410
431
|
const status = `${transaction.status} ${transaction.payment_status}`.toLowerCase();
|
|
411
432
|
const state = status.includes("refund") || status.includes("return") ? "RETURNED" : status.includes("cancel") || status.includes("void") ? "CANCELLED" : "SUCCESS";
|
|
@@ -414,10 +435,10 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
414
435
|
const discount = Math.abs(toMoneyNumber(transaction.discount_amount));
|
|
415
436
|
const voucher = Math.abs(toMoneyNumber(transaction.voucher_discount_amount));
|
|
416
437
|
const voucherRate = toMoneyNumber(transaction.voucher_rate);
|
|
417
|
-
const hasVoucher =
|
|
438
|
+
const hasVoucher = voucher > 0;
|
|
418
439
|
const hasVoucherAndDiscount = isSuccessful && voucher > 0 && discount > 0;
|
|
419
440
|
const scopedVoucherVatNet = voucherVatNet(transaction, voucher, discount);
|
|
420
|
-
const voucherForBirDiscount = -format2(
|
|
441
|
+
const voucherForBirDiscount = itemizedVoucherCount(transaction) > 1 ? -format2(voucher / 1.12) : -format2(
|
|
421
442
|
transaction.voucher_type?.toLowerCase() === "fixed" ? scopedVoucherVatNet - format2(voucherRate) : scopedVoucherVatNet * format2(voucherRate / 100)
|
|
422
443
|
);
|
|
423
444
|
const otherDiscount = isSuccessful && hasVoucher ? format2(voucherForBirDiscount) : 0;
|
|
@@ -481,7 +502,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
481
502
|
vatableSales + vatExemptSales + zeroRatedSales + storedNonTaxableSales + deductions.scDiscount + deductions.pwdDiscount + deductions.naacDiscount + deductions.soloDiscount + deductions.movDiscount + deductions.otherDiscount
|
|
482
503
|
) : 0;
|
|
483
504
|
const serviceCharge = toMoneyNumber(transaction.service_charge_amount);
|
|
484
|
-
const takeoutCharge = toMoneyNumber(transaction.takeout_charge_amount);
|
|
485
505
|
return {
|
|
486
506
|
state,
|
|
487
507
|
receipt: {
|
|
@@ -500,7 +520,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
500
520
|
storedVatSales + storedVatExemptSales + storedZeroRatedSales + storedNonTaxableSales + storedVat
|
|
501
521
|
),
|
|
502
522
|
serviceCharge,
|
|
503
|
-
takeoutCharge,
|
|
504
523
|
amountPayable: toMoneyNumber(transaction.amount_payable)
|
|
505
524
|
},
|
|
506
525
|
report: {
|
|
@@ -512,7 +531,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
512
531
|
zeroRatedSales,
|
|
513
532
|
deductions,
|
|
514
533
|
serviceCharge,
|
|
515
|
-
takeoutCharge,
|
|
516
534
|
adjustments,
|
|
517
535
|
vatPayable: isSuccessful ? Math.max(0, format2(vatAmount - Math.abs(adjustments.totalAdjustments))) : 0,
|
|
518
536
|
netSales,
|
package/dist/index.d.cts
CHANGED
|
@@ -33,6 +33,7 @@ interface CalculateTotalsInput {
|
|
|
33
33
|
*/
|
|
34
34
|
paxDiscountCounts?: PaxDiscountCounts;
|
|
35
35
|
vatRate: number;
|
|
36
|
+
isVatRegistered?: boolean;
|
|
36
37
|
serviceChargeRate: number;
|
|
37
38
|
togoCharge: number;
|
|
38
39
|
}
|
|
@@ -117,7 +118,6 @@ interface FinancialTransactionInput {
|
|
|
117
118
|
zero_rated_sales?: number | string | null;
|
|
118
119
|
non_taxable_sales?: number | string | null;
|
|
119
120
|
service_charge_amount?: number | string | null;
|
|
120
|
-
takeout_charge_amount?: number | string | null;
|
|
121
121
|
discount_customers?: Array<{
|
|
122
122
|
discount_id?: string | null;
|
|
123
123
|
discount_name?: string | null;
|
|
@@ -155,7 +155,6 @@ declare function buildTransactionProjection(transaction: FinancialTransactionInp
|
|
|
155
155
|
vatAmount: number;
|
|
156
156
|
totalSales: number;
|
|
157
157
|
serviceCharge: number;
|
|
158
|
-
takeoutCharge: number;
|
|
159
158
|
amountPayable: number;
|
|
160
159
|
};
|
|
161
160
|
report: {
|
|
@@ -177,7 +176,6 @@ declare function buildTransactionProjection(transaction: FinancialTransactionInp
|
|
|
177
176
|
totalDeductions: number;
|
|
178
177
|
};
|
|
179
178
|
serviceCharge: number;
|
|
180
|
-
takeoutCharge: number;
|
|
181
179
|
adjustments: {
|
|
182
180
|
scDiscountAdjustment: number;
|
|
183
181
|
pwdDiscountAdjustment: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ interface CalculateTotalsInput {
|
|
|
33
33
|
*/
|
|
34
34
|
paxDiscountCounts?: PaxDiscountCounts;
|
|
35
35
|
vatRate: number;
|
|
36
|
+
isVatRegistered?: boolean;
|
|
36
37
|
serviceChargeRate: number;
|
|
37
38
|
togoCharge: number;
|
|
38
39
|
}
|
|
@@ -117,7 +118,6 @@ interface FinancialTransactionInput {
|
|
|
117
118
|
zero_rated_sales?: number | string | null;
|
|
118
119
|
non_taxable_sales?: number | string | null;
|
|
119
120
|
service_charge_amount?: number | string | null;
|
|
120
|
-
takeout_charge_amount?: number | string | null;
|
|
121
121
|
discount_customers?: Array<{
|
|
122
122
|
discount_id?: string | null;
|
|
123
123
|
discount_name?: string | null;
|
|
@@ -155,7 +155,6 @@ declare function buildTransactionProjection(transaction: FinancialTransactionInp
|
|
|
155
155
|
vatAmount: number;
|
|
156
156
|
totalSales: number;
|
|
157
157
|
serviceCharge: number;
|
|
158
|
-
takeoutCharge: number;
|
|
159
158
|
amountPayable: number;
|
|
160
159
|
};
|
|
161
160
|
report: {
|
|
@@ -177,7 +176,6 @@ declare function buildTransactionProjection(transaction: FinancialTransactionInp
|
|
|
177
176
|
totalDeductions: number;
|
|
178
177
|
};
|
|
179
178
|
serviceCharge: number;
|
|
180
|
-
takeoutCharge: number;
|
|
181
179
|
adjustments: {
|
|
182
180
|
scDiscountAdjustment: number;
|
|
183
181
|
pwdDiscountAdjustment: number;
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,11 @@ var splitDiscountTypes = [
|
|
|
36
36
|
{ type: "MOV", count: "movCount", breakdown: "mov", vatExempt: false },
|
|
37
37
|
{ type: "SOLO", count: "soloParentCount", breakdown: "solo", vatExempt: false }
|
|
38
38
|
];
|
|
39
|
+
function resolvedVatDivisor(input) {
|
|
40
|
+
if (input.isVatRegistered === false) return 1;
|
|
41
|
+
const divisor = Number(input.vatRate || 0) / 100;
|
|
42
|
+
return divisor > 0 ? divisor : 1;
|
|
43
|
+
}
|
|
39
44
|
function normalizedCount(value) {
|
|
40
45
|
return Math.max(0, Math.floor(toMoneyNumber(value)));
|
|
41
46
|
}
|
|
@@ -46,7 +51,7 @@ function calculateSplitTotals(input) {
|
|
|
46
51
|
const concessionCount = splitDiscountTypes.reduce((total, item) => total + normalizedCount(counts[item.count]), 0);
|
|
47
52
|
const paxCount = regularCount + concessionCount;
|
|
48
53
|
if (paxCount === 0) return null;
|
|
49
|
-
const vatDivisor =
|
|
54
|
+
const vatDivisor = resolvedVatDivisor(input);
|
|
50
55
|
const vatRate = Math.max(0, vatDivisor - 1);
|
|
51
56
|
const scRate = Number(input.serviceChargeRate || 0) / 100;
|
|
52
57
|
const discountableAmount = Math.max(0, toMoneyNumber(input.discountableTotalAmount));
|
|
@@ -76,6 +81,10 @@ function calculateSplitTotals(input) {
|
|
|
76
81
|
discountBreakdown[item.breakdown] = -discount;
|
|
77
82
|
totalDiscount += discount;
|
|
78
83
|
}
|
|
84
|
+
if (input.isVatRegistered === false) {
|
|
85
|
+
vatExemptSales += vatSales;
|
|
86
|
+
vatSales = 0;
|
|
87
|
+
}
|
|
79
88
|
vatSales = format2(vatSales);
|
|
80
89
|
vatExemptSales = format2(vatExemptSales);
|
|
81
90
|
const discounted = -format2(totalDiscount);
|
|
@@ -90,7 +99,7 @@ function calculateSplitTotals(input) {
|
|
|
90
99
|
return {
|
|
91
100
|
quantity: input.quantity,
|
|
92
101
|
subtotal,
|
|
93
|
-
isAddVat: splitDiscountTypes.some((item) => !item.vatExempt && normalizedCount(counts[item.count]) > 0),
|
|
102
|
+
isAddVat: input.isVatRegistered !== false && splitDiscountTypes.some((item) => !item.vatExempt && normalizedCount(counts[item.count]) > 0),
|
|
94
103
|
vatNet,
|
|
95
104
|
vat,
|
|
96
105
|
discounted,
|
|
@@ -137,7 +146,8 @@ function calculateSplitTotals(input) {
|
|
|
137
146
|
function calculateTotals(input) {
|
|
138
147
|
const splitTotals = calculateSplitTotals(input);
|
|
139
148
|
if (splitTotals) return splitTotals;
|
|
140
|
-
const vatRate =
|
|
149
|
+
const vatRate = resolvedVatDivisor(input);
|
|
150
|
+
const vatPercent = Math.max(0, vatRate - 1);
|
|
141
151
|
const scRate = Number(input.serviceChargeRate || 0) / 100;
|
|
142
152
|
const discountRate = Number(input.discountRate || 0) / 100;
|
|
143
153
|
const voucherRate = input.voucherType === "FIXED" ? input.voucherRate : Number(input.voucherRate || 0) / 100;
|
|
@@ -152,11 +162,11 @@ function calculateTotals(input) {
|
|
|
152
162
|
}
|
|
153
163
|
const subtotal = totalOrderAmount + voucherDiscounted;
|
|
154
164
|
const voucherVatNet2 = format2(totalOrderAmount / vatRate);
|
|
155
|
-
const voucherVat = format2(voucherVatNet2 *
|
|
165
|
+
const voucherVat = format2(voucherVatNet2 * vatPercent);
|
|
156
166
|
let vatNet = format2(subtotal / vatRate);
|
|
157
|
-
let vat = format2(vatNet *
|
|
167
|
+
let vat = format2(vatNet * vatPercent);
|
|
158
168
|
const isSpecial = discountRate > 0;
|
|
159
|
-
const isAddVat = isAddVatAfterDiscount(input.discountType);
|
|
169
|
+
const isAddVat = input.isVatRegistered === false ? false : isAddVatAfterDiscount(input.discountType);
|
|
160
170
|
const discounted = format2(discountRate * -vatNet);
|
|
161
171
|
if (isSpecial) {
|
|
162
172
|
vatNet = format2(vatNet + discounted);
|
|
@@ -373,6 +383,17 @@ function voucherVatNet(transaction, voucher, discount) {
|
|
|
373
383
|
const eligibleGross = itemizedGross > 0 ? itemizedGross : percentageGross > 0 ? percentageGross : gross;
|
|
374
384
|
return format2(Math.min(gross, eligibleGross) / 1.12);
|
|
375
385
|
}
|
|
386
|
+
function itemizedVoucherCount(transaction) {
|
|
387
|
+
const splits = transaction.metadata?.split_discounts;
|
|
388
|
+
if (!Array.isArray(splits)) return 0;
|
|
389
|
+
return splits.reduce((count, rawSplit) => {
|
|
390
|
+
if (!rawSplit || typeof rawSplit !== "object") return count;
|
|
391
|
+
const rawVoucher = rawSplit.voucher;
|
|
392
|
+
if (!rawVoucher || typeof rawVoucher !== "object") return count;
|
|
393
|
+
const voucher = rawVoucher;
|
|
394
|
+
return Math.abs(toMoneyNumber(voucher.discount_amount)) > 0 ? count + 1 : count;
|
|
395
|
+
}, 0);
|
|
396
|
+
}
|
|
376
397
|
function buildTransactionProjection(transaction, options = {}) {
|
|
377
398
|
const status = `${transaction.status} ${transaction.payment_status}`.toLowerCase();
|
|
378
399
|
const state = status.includes("refund") || status.includes("return") ? "RETURNED" : status.includes("cancel") || status.includes("void") ? "CANCELLED" : "SUCCESS";
|
|
@@ -381,10 +402,10 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
381
402
|
const discount = Math.abs(toMoneyNumber(transaction.discount_amount));
|
|
382
403
|
const voucher = Math.abs(toMoneyNumber(transaction.voucher_discount_amount));
|
|
383
404
|
const voucherRate = toMoneyNumber(transaction.voucher_rate);
|
|
384
|
-
const hasVoucher =
|
|
405
|
+
const hasVoucher = voucher > 0;
|
|
385
406
|
const hasVoucherAndDiscount = isSuccessful && voucher > 0 && discount > 0;
|
|
386
407
|
const scopedVoucherVatNet = voucherVatNet(transaction, voucher, discount);
|
|
387
|
-
const voucherForBirDiscount = -format2(
|
|
408
|
+
const voucherForBirDiscount = itemizedVoucherCount(transaction) > 1 ? -format2(voucher / 1.12) : -format2(
|
|
388
409
|
transaction.voucher_type?.toLowerCase() === "fixed" ? scopedVoucherVatNet - format2(voucherRate) : scopedVoucherVatNet * format2(voucherRate / 100)
|
|
389
410
|
);
|
|
390
411
|
const otherDiscount = isSuccessful && hasVoucher ? format2(voucherForBirDiscount) : 0;
|
|
@@ -448,7 +469,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
448
469
|
vatableSales + vatExemptSales + zeroRatedSales + storedNonTaxableSales + deductions.scDiscount + deductions.pwdDiscount + deductions.naacDiscount + deductions.soloDiscount + deductions.movDiscount + deductions.otherDiscount
|
|
449
470
|
) : 0;
|
|
450
471
|
const serviceCharge = toMoneyNumber(transaction.service_charge_amount);
|
|
451
|
-
const takeoutCharge = toMoneyNumber(transaction.takeout_charge_amount);
|
|
452
472
|
return {
|
|
453
473
|
state,
|
|
454
474
|
receipt: {
|
|
@@ -467,7 +487,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
467
487
|
storedVatSales + storedVatExemptSales + storedZeroRatedSales + storedNonTaxableSales + storedVat
|
|
468
488
|
),
|
|
469
489
|
serviceCharge,
|
|
470
|
-
takeoutCharge,
|
|
471
490
|
amountPayable: toMoneyNumber(transaction.amount_payable)
|
|
472
491
|
},
|
|
473
492
|
report: {
|
|
@@ -479,7 +498,6 @@ function buildTransactionProjection(transaction, options = {}) {
|
|
|
479
498
|
zeroRatedSales,
|
|
480
499
|
deductions,
|
|
481
500
|
serviceCharge,
|
|
482
|
-
takeoutCharge,
|
|
483
501
|
adjustments,
|
|
484
502
|
vatPayable: isSuccessful ? Math.max(0, format2(vatAmount - Math.abs(adjustments.totalAdjustments))) : 0,
|
|
485
503
|
netSales,
|