foodbot-cart-calculations 1.0.62 → 1.0.64

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.
@@ -84,9 +84,9 @@ function calculateTax2(cart, texSettings, taxType) {
84
84
  }, 300)
85
85
  });
86
86
 
87
- calculateItemWiseSplit(cart).then((cart) => {
87
+ // calculateItemWiseSplit(cart).then((cart) => {
88
88
  resolve(cart);
89
- });
89
+ // });
90
90
  }
91
91
  });
92
92
  } catch (error) {
@@ -806,4 +806,192 @@ async function calculateItemWiseSplit(cart, itemWiseItems) {
806
806
  });
807
807
  }
808
808
 
809
- module.exports = { calculateTax2, calculateItemWiseSplit }
809
+ // calculateHalfSplit.js
810
+ /**
811
+ * Calculate half-split (or N-equal split for unpaid customers) with rounding adjustments.
812
+ *
813
+ * Parameters:
814
+ * - cart: object containing order, split_payments etc. (mutated and returned)
815
+ * - selectedMembers: (not used directly in calculation here, kept for parity with caller)
816
+ * - options: optional object {
817
+ * itemWiseItems, // array used to derive per-item tax/prices (falls back to cart.itemWiseItems)
818
+ * allItems, // template items for each split (falls back to cart.order.items or cart.items)
819
+ * halfSplitTaxPerPerson, // optional precomputed per-person tax (Number)
820
+ * halfSplitTotalPerPerson // optional precomputed per-person amount (Number)
821
+ * }
822
+ *
823
+ * Returns:
824
+ * - resolved Promise with updated cart
825
+ */
826
+ async function calculateHalfSplit(cart, options = {}) {
827
+ return new Promise(async (resolve, reject) => {
828
+ try {
829
+ // Safe getters / defaults
830
+ const itemWiseItems = options.itemWiseItems || [];
831
+ const allItems = options.allItems || (cart && cart.items) || [];
832
+ const splits = cart.split_payments || [];
833
+
834
+ // Totals
835
+ const totalAmount = parseFloat((cart.after_discount) ?? 0) || 0;
836
+ const totalTax = parseFloat((cart.tax_amount) ?? 0) || 0;
837
+
838
+ // Already paid sums
839
+ const paidSplits = splits.filter((p) => p.paid_status);
840
+ const paidAmount = paidSplits.reduce((sum, p) => sum + (parseFloat(p.amount || '0') || 0), 0);
841
+ const paidTax = paidSplits.reduce((sum, p) => sum + (parseFloat(p.tax || '0') || 0), 0);
842
+
843
+ // Unpaid totals
844
+ const unpaidTotalAmount = totalAmount - paidAmount;
845
+ const unpaidTotalTax = totalTax - paidTax;
846
+
847
+ // Unpaid split count
848
+ const unpaidSplits = splits.filter((p) => !p.paid_status);
849
+ const unpaidCount = unpaidSplits.length || 1; // avoid division by zero
850
+
851
+ // If caller provided per-person values use them, otherwise compute equal split per unpaid person
852
+ let halfSplitTaxPerPerson = (typeof options.halfSplitTaxPerPerson == 'number')
853
+ ? options.halfSplitTaxPerPerson
854
+ : unpaidTotalTax / unpaidCount;
855
+
856
+ let halfSplitTotalPerPerson = (typeof options.halfSplitTotalPerPerson == 'number')
857
+ ? options.halfSplitTotalPerPerson
858
+ : unpaidTotalAmount / unpaidCount;
859
+
860
+ // Keep accumulators for rounding remainder
861
+ let accumulatedAmount = 0;
862
+ let accumulatedTax = 0;
863
+ let unpaidIndex = 0;
864
+
865
+ // For each split payment: assign items, compute item-level taxes & subtotal, then apply split rounding rules
866
+ for (let p of splits) {
867
+ if (!p.paid_status) {
868
+ const isLastUnpaid = unpaidIndex == unpaidCount - 1;
869
+
870
+ // clone items so we don't mutate shared object
871
+ p.items = JSON.parse(JSON.stringify(allItems));
872
+
873
+ // Recompute item-level tax/amount similar to your itemwise logic
874
+ let subtotal = 0;
875
+ let tax = 0;
876
+
877
+ for (let pi of p.items) {
878
+ const item = itemWiseItems.find((i) => i._temp_id == pi._temp_id);
879
+ if (!item) continue;
880
+
881
+ const qty = Number(pi.quantity || 0);
882
+ const price = Number(item.per_item_cost || 0);
883
+ const itemTaxTotal = Number(item.total_tax_amount || 0);
884
+
885
+ // quantity-wise tax array
886
+ const quantityWiseTaxArray = (item.tax_array || []).map((taxObj) => {
887
+ const unitBase = Number(taxObj.base_price || 0) / (Number(item.quantity) || 1);
888
+ const unitTaxAmount = Number(taxObj.tax_amount || 0) / (Number(item.quantity) || 1);
889
+ return {
890
+ ...taxObj,
891
+ base_price: (unitBase * qty).toFixed(6),
892
+ tax_amount: (unitTaxAmount * qty).toFixed(6),
893
+ };
894
+ });
895
+
896
+ // modifiers
897
+ let modifierTaxArray = [];
898
+ const mods = item.item_modifiers || item.modifiers || [];
899
+ for (let m of mods) {
900
+ const mq = Number(m.quantity || 0) || 1;
901
+ const modTax = (m.tax_array || []).map((tx) => {
902
+ const unitBase = Number(tx.base_price || 0) / ((Number(item.quantity) || 1) * mq);
903
+ const unitTax = Number(tx.tax_amount || 0) / ((Number(item.quantity) || 1) * mq);
904
+ return {
905
+ ...tx,
906
+ base_price: (unitBase * (mq * qty)).toFixed(6),
907
+ tax_amount: (unitTax * (mq * qty)).toFixed(6),
908
+ };
909
+ });
910
+ modifierTaxArray.push(...modTax);
911
+ }
912
+
913
+ // merge item-level taxes by tax_id
914
+ const mergedItemTaxMap = {};
915
+ for (let t of [...quantityWiseTaxArray, ...modifierTaxArray]) {
916
+ if (!mergedItemTaxMap[t.tax_id]) {
917
+ mergedItemTaxMap[t.tax_id] = { ...t, base_price: 0, tax_amount: 0 };
918
+ }
919
+ mergedItemTaxMap[t.tax_id].base_price += Number(t.base_price || 0);
920
+ mergedItemTaxMap[t.tax_id].tax_amount += Number(t.tax_amount || 0);
921
+ }
922
+
923
+ pi.tax_array = Object.values(mergedItemTaxMap).map((i) => ({
924
+ ...i,
925
+ base_price: Number(i.base_price).toFixed(6),
926
+ tax_amount: Number(i.tax_amount).toFixed(6),
927
+ }));
928
+
929
+ // subtotal and tax contribution for this item in this split
930
+ subtotal += price * qty;
931
+ tax += (itemTaxTotal / (Number(item.quantity) || 1)) * qty;
932
+ } // end items loop
933
+
934
+ // Merge tax_array across items for this split
935
+ const mergedTaxMap = {};
936
+ for (let pi of p.items) {
937
+ (pi.tax_array || []).forEach((t) => {
938
+ if (!mergedTaxMap[t.tax_id]) {
939
+ mergedTaxMap[t.tax_id] = { ...t, base_price: 0, tax_amount: 0 };
940
+ }
941
+ mergedTaxMap[t.tax_id].base_price += Number(t.base_price || 0);
942
+ mergedTaxMap[t.tax_id].tax_amount += Number(t.tax_amount || 0);
943
+ });
944
+ }
945
+
946
+ p.merge_tax_array = Object.values(mergedTaxMap).map((t) => ({
947
+ ...t,
948
+ base_price: Number(t.base_price).toFixed(6),
949
+ tax_amount: Number(t.tax_amount).toFixed(6),
950
+ }));
951
+
952
+ // Apply equal split amounts with rounding adjustment:
953
+ if (isLastUnpaid) {
954
+ // last unpaid gets the remainder (unpaid totals minus accumulated assigned amounts)
955
+ // Use arithmetic with Number() and then format to 2 decimals for amount/tax
956
+ const remainingTax = (unpaidTotalTax - accumulatedTax);
957
+ const remainingAmount = (unpaidTotalAmount - accumulatedAmount);
958
+
959
+ p.tax = Number(remainingTax).toFixed(2);
960
+ p.amount = Number(remainingAmount).toFixed(2);
961
+ p.due = p.amount;
962
+ } else {
963
+ // Non-last unpaid: assign precomputed per-person values (rounded to 2 decimals)
964
+ const taxAmount = Number(Number(halfSplitTaxPerPerson).toFixed(2));
965
+ const dueAmount = Number(Number(halfSplitTotalPerPerson).toFixed(2));
966
+
967
+ p.tax = taxAmount.toFixed(2);
968
+ p.amount = dueAmount.toFixed(2);
969
+ p.due = dueAmount.toFixed(2);
970
+
971
+ accumulatedTax += taxAmount;
972
+ accumulatedAmount += dueAmount;
973
+ }
974
+
975
+ unpaidIndex++;
976
+ } // end if unpaid
977
+ } // end for splits loop
978
+
979
+ // Attach updated splits back to cart (if different structure)
980
+ if (cart.split_payments) {
981
+ cart.split_payments = splits;
982
+ }
983
+ resolve(cart);
984
+ } catch (err) {
985
+ // bubble up so callers can handle
986
+ console.error("❌ Split Payment Calculation Failed:", err);
987
+ resolve({
988
+ 'status': 'error',
989
+ 'status_code': 500,
990
+ 'message': 'Internal server error',
991
+ 'error': error.stack,
992
+ });
993
+ }
994
+ });
995
+ }
996
+
997
+ module.exports = { calculateTax2, calculateItemWiseSplit, calculateHalfSplit }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const calculation = require('./calculations');
2
- const { calculateItemWiseSplit } = require('./functions/taxCalculation');
2
+ const { calculateItemWiseSplit, calculateHalfSplit } = require('./functions/taxCalculation');
3
3
  // const fs = require('fs');
4
4
  // const path = require('path')
5
5
 
@@ -76,7 +76,7 @@ async function calculateTax(inputJSON) {
76
76
  })
77
77
  }
78
78
 
79
- // let json = JSON.parse(fs.readFileSync('./sample.json', 'utf8'));
79
+ let json = JSON.parse(fs.readFileSync('./sample.json', 'utf8'));
80
80
 
81
81
  // calculateTax(json).then(res=>{
82
82
  // // console.log(JSON.stringify(res))
@@ -114,4 +114,4 @@ async function calculateTax(inputJSON) {
114
114
  // console.log(err)
115
115
  // })
116
116
 
117
- module.exports = { calculateTax , calculateItemWiseSplit}
117
+ module.exports = { calculateTax , calculateItemWiseSplit, calculateHalfSplit}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foodbot-cart-calculations",
3
- "version": "1.0.62",
3
+ "version": "1.0.64",
4
4
  "description": "Package for cart calculations in which it performs discount distribution and tax distribution on order",
5
5
  "main": "index.js",
6
6
  "scripts": {