@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 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 = Number(input.vatRate || 0) / 100;
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 = Number(input.vatRate || 0) / 100;
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 * 0.12);
198
+ const voucherVat = format2(voucherVatNet2 * vatPercent);
189
199
  let vatNet = format2(subtotal / vatRate);
190
- let vat = format2(vatNet * 0.12);
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 = voucherRate > 0;
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 = Number(input.vatRate || 0) / 100;
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 = Number(input.vatRate || 0) / 100;
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 * 0.12);
165
+ const voucherVat = format2(voucherVatNet2 * vatPercent);
156
166
  let vatNet = format2(subtotal / vatRate);
157
- let vat = format2(vatNet * 0.12);
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 = voucherRate > 0;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qrwise/pos-calculations",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "Shared POS calculations for QR Wise POS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",