foodbot-cart-calculations 1.0.67 → 1.0.68
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/functions/taxCalculation.js +110 -80
- package/package.json +1 -1
|
@@ -829,172 +829,202 @@ async function calculateItemWiseSplit(cart, itemWiseItems) {
|
|
|
829
829
|
async function calculateHalfSplit(cart, options = {}) {
|
|
830
830
|
return new Promise(async (resolve, reject) => {
|
|
831
831
|
try {
|
|
832
|
-
// Safe getters / defaults
|
|
833
832
|
const itemWiseItems = options.itemWiseItems || [];
|
|
834
|
-
const allItems = options.allItems ||
|
|
833
|
+
const allItems = options.allItems || cart.items || [];
|
|
835
834
|
const splits = cart.split_payments || [];
|
|
836
835
|
|
|
837
836
|
// Totals
|
|
838
|
-
const totalAmount = parseFloat(
|
|
839
|
-
const totalTax = parseFloat(
|
|
837
|
+
const totalAmount = parseFloat(cart.after_discount ?? 0) || 0;
|
|
838
|
+
const totalTax = parseFloat(cart.tax_amount ?? 0) || 0;
|
|
840
839
|
|
|
841
|
-
//
|
|
840
|
+
// Paid amounts
|
|
842
841
|
const paidSplits = splits.filter((p) => p.paid_status);
|
|
843
|
-
const paidAmount = paidSplits.reduce((sum, p) => sum + (parseFloat(p.amount ||
|
|
844
|
-
const paidTax = paidSplits.reduce((sum, p) => sum + (parseFloat(p.tax ||
|
|
842
|
+
const paidAmount = paidSplits.reduce((sum, p) => sum + (parseFloat(p.amount || 0)), 0);
|
|
843
|
+
const paidTax = paidSplits.reduce((sum, p) => sum + (parseFloat(p.tax || 0)), 0);
|
|
845
844
|
|
|
846
845
|
// Unpaid totals
|
|
847
846
|
const unpaidTotalAmount = totalAmount - paidAmount;
|
|
848
847
|
const unpaidTotalTax = totalTax - paidTax;
|
|
849
848
|
|
|
850
|
-
// Unpaid split count
|
|
851
849
|
const unpaidSplits = splits.filter((p) => !p.paid_status);
|
|
852
|
-
const unpaidCount = unpaidSplits.length || 1;
|
|
850
|
+
const unpaidCount = unpaidSplits.length || 1;
|
|
853
851
|
|
|
854
|
-
//
|
|
855
|
-
let halfSplitTaxPerPerson =
|
|
856
|
-
|
|
857
|
-
|
|
852
|
+
// per-person distribution
|
|
853
|
+
let halfSplitTaxPerPerson =
|
|
854
|
+
typeof options.halfSplitTaxPerPerson == "number"
|
|
855
|
+
? options.halfSplitTaxPerPerson
|
|
856
|
+
: unpaidTotalTax / unpaidCount;
|
|
858
857
|
|
|
859
|
-
let halfSplitTotalPerPerson =
|
|
860
|
-
|
|
861
|
-
|
|
858
|
+
let halfSplitTotalPerPerson =
|
|
859
|
+
typeof options.halfSplitTotalPerPerson == "number"
|
|
860
|
+
? options.halfSplitTotalPerPerson
|
|
861
|
+
: unpaidTotalAmount / unpaidCount;
|
|
862
862
|
|
|
863
|
-
// Keep accumulators for rounding remainder
|
|
864
863
|
let accumulatedAmount = 0;
|
|
865
864
|
let accumulatedTax = 0;
|
|
866
865
|
let unpaidIndex = 0;
|
|
867
866
|
|
|
868
|
-
// For each split payment: assign items, compute item-level taxes & subtotal, then apply split rounding rules
|
|
869
867
|
for (let p of splits) {
|
|
870
868
|
if (!p.paid_status) {
|
|
871
|
-
const isLastUnpaid = unpaidIndex
|
|
869
|
+
const isLastUnpaid = unpaidIndex === unpaidCount - 1;
|
|
872
870
|
|
|
873
|
-
//
|
|
871
|
+
// Clone all items for this split user
|
|
874
872
|
p.items = JSON.parse(JSON.stringify(allItems));
|
|
875
873
|
|
|
876
|
-
//
|
|
874
|
+
// --------------------------------------------------------
|
|
875
|
+
// 🔥 ADDED LOGIC — Adjust quantities for the LAST UNPAID customer
|
|
876
|
+
// --------------------------------------------------------
|
|
877
|
+
if (isLastUnpaid) {
|
|
878
|
+
p.items.forEach((item) => {
|
|
879
|
+
const origin = itemWiseItems.find((it) => it._temp_id === item._temp_id);
|
|
880
|
+
if (!origin) return;
|
|
881
|
+
|
|
882
|
+
// 1. Compute how many qty PAID customers already took
|
|
883
|
+
let paidQty = 0;
|
|
884
|
+
splits.forEach((sp) => {
|
|
885
|
+
if (sp.paid_status && sp.items) {
|
|
886
|
+
const paidItem = sp.items.find((pi) => pi._temp_id === item._temp_id);
|
|
887
|
+
if (paidItem) paidQty += parseFloat(paidItem.quantity || 0);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
// 2. Remaining qty for unpaid
|
|
892
|
+
const remainingQty = origin.quantity - paidQty;
|
|
893
|
+
|
|
894
|
+
// 3. Quantity each unpaid gets
|
|
895
|
+
const qtyPerUnpaid = parseFloat((remainingQty / unpaidCount).toFixed(6));
|
|
896
|
+
|
|
897
|
+
// 4. All other unpaid customers qty allocated
|
|
898
|
+
const allocatedToOthers = qtyPerUnpaid * (unpaidCount - 1);
|
|
899
|
+
|
|
900
|
+
// 5. FINAL remainder → assign to *last unpaid customer*
|
|
901
|
+
const finalQty = parseFloat((remainingQty - allocatedToOthers).toFixed(6));
|
|
902
|
+
|
|
903
|
+
item.quantity = finalQty;
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// --------------------------------------------------------
|
|
908
|
+
// RECOMPUTE subtotal + taxes with UPDATED quantities
|
|
909
|
+
// --------------------------------------------------------
|
|
877
910
|
let subtotal = 0;
|
|
878
911
|
let tax = 0;
|
|
879
912
|
|
|
880
913
|
for (let pi of p.items) {
|
|
881
|
-
const item = itemWiseItems.find((i) => i._temp_id
|
|
914
|
+
const item = itemWiseItems.find((i) => i._temp_id === pi._temp_id);
|
|
882
915
|
if (!item) continue;
|
|
883
916
|
|
|
884
917
|
const qty = Number(pi.quantity || 0);
|
|
885
918
|
const price = Number(item.per_item_cost || 0);
|
|
886
919
|
const itemTaxTotal = Number(item.total_tax_amount || 0);
|
|
887
920
|
|
|
888
|
-
// quantity-wise tax array
|
|
889
921
|
const quantityWiseTaxArray = (item.tax_array || []).map((taxObj) => {
|
|
890
|
-
const unitBase = Number(taxObj.base_price
|
|
891
|
-
const
|
|
922
|
+
const unitBase = Number(taxObj.base_price) / (item.quantity || 1);
|
|
923
|
+
const unitTax = Number(taxObj.tax_amount) / (item.quantity || 1);
|
|
924
|
+
|
|
892
925
|
return {
|
|
893
926
|
...taxObj,
|
|
894
|
-
base_price: (unitBase * qty).toFixed(
|
|
895
|
-
tax_amount: (
|
|
927
|
+
base_price: (unitBase * qty).toFixed(toFix),
|
|
928
|
+
tax_amount: (unitTax * qty).toFixed(toFix),
|
|
896
929
|
};
|
|
897
930
|
});
|
|
898
931
|
|
|
899
|
-
// modifiers
|
|
932
|
+
// modifiers tax
|
|
900
933
|
let modifierTaxArray = [];
|
|
901
934
|
const mods = item.item_modifiers || item.modifiers || [];
|
|
902
|
-
|
|
935
|
+
|
|
936
|
+
mods.forEach((m) => {
|
|
903
937
|
const mq = Number(m.quantity || 0) || 1;
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
const
|
|
938
|
+
|
|
939
|
+
const modTax = m.tax_array.map((tx) => {
|
|
940
|
+
const unitBase = Number(tx.base_price) / (item.quantity * mq);
|
|
941
|
+
const unitTax = Number(tx.tax_amount) / (item.quantity * mq);
|
|
942
|
+
|
|
907
943
|
return {
|
|
908
944
|
...tx,
|
|
909
|
-
base_price: (unitBase * (mq * qty)).toFixed(
|
|
910
|
-
tax_amount: (unitTax * (mq * qty)).toFixed(
|
|
945
|
+
base_price: (unitBase * (mq * qty)).toFixed(toFix),
|
|
946
|
+
tax_amount: (unitTax * (mq * qty)).toFixed(toFix),
|
|
911
947
|
};
|
|
912
948
|
});
|
|
949
|
+
|
|
913
950
|
modifierTaxArray.push(...modTax);
|
|
914
|
-
}
|
|
951
|
+
});
|
|
915
952
|
|
|
916
|
-
//
|
|
953
|
+
// Merge item taxes
|
|
917
954
|
const mergedItemTaxMap = {};
|
|
918
|
-
|
|
955
|
+
[...quantityWiseTaxArray, ...modifierTaxArray].forEach((t) => {
|
|
919
956
|
if (!mergedItemTaxMap[t.tax_id]) {
|
|
920
957
|
mergedItemTaxMap[t.tax_id] = { ...t, base_price: 0, tax_amount: 0 };
|
|
921
958
|
}
|
|
922
|
-
mergedItemTaxMap[t.tax_id].base_price += Number(t.base_price
|
|
923
|
-
mergedItemTaxMap[t.tax_id].tax_amount += Number(t.tax_amount
|
|
924
|
-
}
|
|
959
|
+
mergedItemTaxMap[t.tax_id].base_price += Number(t.base_price);
|
|
960
|
+
mergedItemTaxMap[t.tax_id].tax_amount += Number(t.tax_amount);
|
|
961
|
+
});
|
|
925
962
|
|
|
926
963
|
pi.tax_array = Object.values(mergedItemTaxMap).map((i) => ({
|
|
927
964
|
...i,
|
|
928
|
-
base_price: Number(i.base_price).toFixed(
|
|
929
|
-
tax_amount: Number(i.tax_amount).toFixed(
|
|
965
|
+
base_price: Number(i.base_price).toFixed(toFix),
|
|
966
|
+
tax_amount: Number(i.tax_amount).toFixed(toFix),
|
|
930
967
|
}));
|
|
931
968
|
|
|
932
|
-
// subtotal and tax contribution for this item in this split
|
|
933
969
|
subtotal += price * qty;
|
|
934
|
-
tax += (itemTaxTotal /
|
|
935
|
-
}
|
|
970
|
+
tax += (itemTaxTotal / item.quantity) * qty;
|
|
971
|
+
}
|
|
936
972
|
|
|
937
|
-
// Merge
|
|
973
|
+
// Merge taxes for whole split
|
|
938
974
|
const mergedTaxMap = {};
|
|
939
|
-
|
|
975
|
+
p.items.forEach((pi) => {
|
|
940
976
|
(pi.tax_array || []).forEach((t) => {
|
|
941
977
|
if (!mergedTaxMap[t.tax_id]) {
|
|
942
978
|
mergedTaxMap[t.tax_id] = { ...t, base_price: 0, tax_amount: 0 };
|
|
943
979
|
}
|
|
944
|
-
mergedTaxMap[t.tax_id].base_price += Number(t.base_price
|
|
945
|
-
mergedTaxMap[t.tax_id].tax_amount += Number(t.tax_amount
|
|
980
|
+
mergedTaxMap[t.tax_id].base_price += Number(t.base_price);
|
|
981
|
+
mergedTaxMap[t.tax_id].tax_amount += Number(t.tax_amount);
|
|
946
982
|
});
|
|
947
|
-
}
|
|
983
|
+
});
|
|
948
984
|
|
|
949
985
|
p.merge_tax_array = Object.values(mergedTaxMap).map((t) => ({
|
|
950
986
|
...t,
|
|
951
|
-
base_price: Number(t.base_price).toFixed(
|
|
952
|
-
tax_amount: Number(t.tax_amount).toFixed(
|
|
987
|
+
base_price: Number(t.base_price).toFixed(toFix),
|
|
988
|
+
tax_amount: Number(t.tax_amount).toFixed(toFix),
|
|
953
989
|
}));
|
|
954
990
|
|
|
955
|
-
//
|
|
991
|
+
// --------------------------------------------------------
|
|
992
|
+
// APPLY SPLIT AMOUNT + REMAINDER HANDLING
|
|
993
|
+
// --------------------------------------------------------
|
|
956
994
|
if (isLastUnpaid) {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
const remainingTax = (unpaidTotalTax - accumulatedTax);
|
|
960
|
-
const remainingAmount = (unpaidTotalAmount - accumulatedAmount);
|
|
961
|
-
|
|
962
|
-
p.tax = Number(remainingTax).toFixed(toFix);
|
|
963
|
-
p.amount = Number(remainingAmount).toFixed(toFix);
|
|
995
|
+
p.tax = Number(unpaidTotalTax - accumulatedTax).toFixed(toFix);
|
|
996
|
+
p.amount = Number(unpaidTotalAmount - accumulatedAmount).toFixed(toFix);
|
|
964
997
|
p.due = p.amount;
|
|
965
998
|
} else {
|
|
966
|
-
|
|
967
|
-
const
|
|
968
|
-
const dueAmount = Number(Number(halfSplitTotalPerPerson).toFixed(toFix));
|
|
999
|
+
const taxAmount = Number(halfSplitTaxPerPerson).toFixed(toFix);
|
|
1000
|
+
const amountPer = Number(halfSplitTotalPerPerson).toFixed(toFix);
|
|
969
1001
|
|
|
970
|
-
p.tax = taxAmount
|
|
971
|
-
p.amount =
|
|
972
|
-
p.due =
|
|
1002
|
+
p.tax = taxAmount;
|
|
1003
|
+
p.amount = amountPer;
|
|
1004
|
+
p.due = amountPer;
|
|
973
1005
|
|
|
974
|
-
accumulatedTax += taxAmount;
|
|
975
|
-
accumulatedAmount +=
|
|
1006
|
+
accumulatedTax += Number(taxAmount);
|
|
1007
|
+
accumulatedAmount += Number(amountPer);
|
|
976
1008
|
}
|
|
977
1009
|
|
|
978
1010
|
unpaidIndex++;
|
|
979
|
-
}
|
|
980
|
-
} // end for splits loop
|
|
981
|
-
|
|
982
|
-
// Attach updated splits back to cart (if different structure)
|
|
983
|
-
if (cart.split_payments) {
|
|
984
|
-
cart.split_payments = splits;
|
|
1011
|
+
}
|
|
985
1012
|
}
|
|
1013
|
+
|
|
1014
|
+
cart.split_payments = splits;
|
|
986
1015
|
resolve(cart);
|
|
1016
|
+
|
|
987
1017
|
} catch (err) {
|
|
988
|
-
|
|
989
|
-
console.error("❌ Split Payment Calculation Failed:", err);
|
|
1018
|
+
console.error("❌ HalfSplit Calculation Error:", err);
|
|
990
1019
|
resolve({
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1020
|
+
status: "error",
|
|
1021
|
+
status_code: 500,
|
|
1022
|
+
message: "Internal server error",
|
|
1023
|
+
error: err.stack,
|
|
995
1024
|
});
|
|
996
1025
|
}
|
|
997
1026
|
});
|
|
998
1027
|
}
|
|
999
1028
|
|
|
1029
|
+
|
|
1000
1030
|
module.exports = { calculateTax2, calculateItemWiseSplit, calculateHalfSplit }
|
package/package.json
CHANGED