134 lines
4.1 KiB
React
134 lines
4.1 KiB
React
|
|
const toNum = (v, fallback = 0) => (v == null ? fallback : Number(v));
|
||
|
|
|
||
|
|
export function validateOffers(
|
||
|
|
item,
|
||
|
|
OverAllproductBasedOfferDatas,
|
||
|
|
CartData = [],
|
||
|
|
TakAwayId,
|
||
|
|
DineinId
|
||
|
|
) {
|
||
|
|
const qty = toNum(item.OrderQty);
|
||
|
|
const amount = toNum(item.TotalAmt);
|
||
|
|
|
||
|
|
const allOffers =
|
||
|
|
(OverAllproductBasedOfferDatas?.ProdDtl || []).filter(
|
||
|
|
(offer) =>
|
||
|
|
offer.OfferType !== "S" &&
|
||
|
|
offer.ProdId === item?.ProdId &&
|
||
|
|
(offer.LimitUsage > 0 || offer.LimitUsage == null) &&
|
||
|
|
(offer.ProdVariantDetails || []).some((variant) =>
|
||
|
|
(variant.StockDetails || []).some(
|
||
|
|
(stock) => stock.InwardDtlId === item?.InwardDtlId
|
||
|
|
)
|
||
|
|
)
|
||
|
|
) || [];
|
||
|
|
|
||
|
|
// filter by offer applicability
|
||
|
|
const applicableOffers = allOffers.filter((offer) => {
|
||
|
|
if (offer.OfferType === "I") {
|
||
|
|
return amount >= (offer.MinPurchase || 0) || qty >= (offer.MinQty || 0);
|
||
|
|
}
|
||
|
|
if (offer.OfferType === "Q") {
|
||
|
|
return qty >= (offer.FromQty || 0) && qty <= (offer.ToQty || Infinity);
|
||
|
|
}
|
||
|
|
if (offer.OfferType === "B") {
|
||
|
|
const cartQty = CartData?.filter((c) => c.ProdId === item.ProdId).reduce(
|
||
|
|
(acc, cur) => acc + toNum(cur.OrderQty),
|
||
|
|
0
|
||
|
|
);
|
||
|
|
return cartQty >= (offer.MinQty || 0);
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
});
|
||
|
|
|
||
|
|
// separate B (buy/get) from others
|
||
|
|
const buyGetOffers = [];
|
||
|
|
let bestNonB = null;
|
||
|
|
let bestDiscount = 0;
|
||
|
|
|
||
|
|
for (const offer of applicableOffers) {
|
||
|
|
if (offer.OfferType === "B") buyGetOffers.push(offer);
|
||
|
|
else {
|
||
|
|
const discount =
|
||
|
|
offer.ValueType === "F"
|
||
|
|
? toNum(offer.OfferAmt)
|
||
|
|
: (toNum(offer.OfferAmt) * amount) / 100;
|
||
|
|
if (discount >= bestDiscount) {
|
||
|
|
bestDiscount = discount;
|
||
|
|
bestNonB = offer;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// dedupe circular buy/get relationships (keep simpler: remove offers that are circular duplicates)
|
||
|
|
const dedupedBuyGet = buyGetOffers.filter((o, idx, arr) => {
|
||
|
|
if (!o.FreeProducts && !o.FreeProductsList) return true;
|
||
|
|
return !o.FreeProductsList?.some((fp) =>
|
||
|
|
arr.some(
|
||
|
|
(other) =>
|
||
|
|
other !== o &&
|
||
|
|
other.ProdId === fp.FreeProdId &&
|
||
|
|
other.FreeProductsList?.some((ofp) => ofp.FreeProdId === o.ProdId)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
const results = [];
|
||
|
|
if (dedupedBuyGet.length > 0) {
|
||
|
|
for (const o of dedupedBuyGet)
|
||
|
|
results.push(buildOfferObj(o, o.OfferAmt || 0, item, DineinId, TakAwayId));
|
||
|
|
} else if (bestNonB) {
|
||
|
|
results.push(
|
||
|
|
buildOfferObj(bestNonB, bestDiscount, item, DineinId, TakAwayId)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return results;
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildOfferObj(offer, discount, item, DineinId, TakAwayId) {
|
||
|
|
const freeProductsRaw = offer.FreeProducts || offer.FreeProductsList;
|
||
|
|
const freeProductsArray = freeProductsRaw
|
||
|
|
? Array.isArray(freeProductsRaw)
|
||
|
|
? freeProductsRaw
|
||
|
|
: [freeProductsRaw]
|
||
|
|
: [];
|
||
|
|
|
||
|
|
return {
|
||
|
|
OfferId: offer.OfferId || "",
|
||
|
|
OfferAmount: discount || 0,
|
||
|
|
OfferMode: offer.OfferType, // I/Q/B
|
||
|
|
AcuvalOfferAmount: offer.OfferAmt,
|
||
|
|
OfferValue: offer.OfferAmt || 0,
|
||
|
|
TableName: offer.TableName || "",
|
||
|
|
TableUniqueName: offer.TableUniqueName || "",
|
||
|
|
TableUniqueId: offer.TableUniqueId || "",
|
||
|
|
ActualFreeQty: offer.ActualFreeQty || 0,
|
||
|
|
OfferType: offer.ValueType || "", // F / P
|
||
|
|
Detail: offer.Detail || [],
|
||
|
|
OfferCode: offer.OfferCode || "",
|
||
|
|
CustId: offer.CustId || "",
|
||
|
|
UsedCount: offer.UsedCount || 0,
|
||
|
|
AppliesTo: offer.AppliesTo || "",
|
||
|
|
BookingType: item.BookingTypeName === "Dine In" ? DineinId : TakAwayId,
|
||
|
|
FreeProductsList: freeProductsArray.map((fp) => ({
|
||
|
|
FreeProdId: fp.FreeProdId || "",
|
||
|
|
FreeQty: fp.FreeQty || 0,
|
||
|
|
FreeProdName: fp.FreeProdName || "",
|
||
|
|
ProdVariantDetails: fp.ProdVariantDetails || [],
|
||
|
|
})),
|
||
|
|
// product-level fields
|
||
|
|
ProdId: item?.ProdId || "",
|
||
|
|
SinglePc: item?.SinglePc || "",
|
||
|
|
StockAvailable: item?.StockAvailable || "",
|
||
|
|
ProdVariantName: item?.ProdVariantName || "",
|
||
|
|
Type: item?.Type || "",
|
||
|
|
BookingType: item?.BookingType || 0,
|
||
|
|
InwardDtlId: item?.InwardDtlId || "",
|
||
|
|
ProdCat: item?.ProdCat || "",
|
||
|
|
HalfPrice: offer?.HalfPrice,
|
||
|
|
MinQty: offer?.MinQty,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|