From ee348f967a0c563fe95216787de39b827a31553b Mon Sep 17 00:00:00 2001 From: AbleKSaju <126228406+AbleKSaju@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:08:11 +0530 Subject: [PATCH] fix: prevent applying pricing rule for free items when stock is insufficient --- models/baseModels/Invoice/Invoice.ts | 27 ++++++++++---------- models/helpers.ts | 38 +++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index d345a577..71fd1490 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -1264,17 +1264,6 @@ export abstract class Invoice extends Transactional { continue; } - const canApplyPRLOnItem = canApplyPricingRule( - pricingRuleDoc, - this.date as Date, - item.quantity as number, - item.amount as Money - ); - - if (!canApplyPRLOnItem) { - continue; - } - let freeItemQty = pricingRuleDoc.freeItemQuantity as number; if (pricingRuleDoc.isRecursive) { @@ -1282,6 +1271,18 @@ export abstract class Invoice extends Transactional { (item.quantity as number) / (pricingRuleDoc.recurseEvery as number); } + const canApplyPRLOnItem = canApplyPricingRule( + pricingRuleDoc, + this.date as Date, + item.quantity as number, + item.amount as Money, + freeItemQty + ); + + if (!canApplyPRLOnItem) { + continue; + } + if (pricingRuleDoc.roundFreeItemQty) { freeItemQty = roundFreeItemQty( freeItemQty, @@ -1437,14 +1438,14 @@ export abstract class Invoice extends Transactional { } } - const filtered = filterPricingRules( + const filtered = await filterPricingRules( pricingRuleDocsForItem, this.date as Date, item.quantity as number, item.amount as Money ); - if (!filtered.length) { + if (!filtered || !filtered.length) { continue; } diff --git a/models/helpers.ts b/models/helpers.ts index f6ee08e7..0f21b970 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -39,6 +39,7 @@ import { safeParseFloat } from 'utils/index'; import { PriceList } from './baseModels/PriceList/PriceList'; import { InvoiceItem } from './baseModels/InvoiceItem/InvoiceItem'; import { SalesInvoiceItem } from './baseModels/SalesInvoiceItem/SalesInvoiceItem'; +import { getItemQtyMap } from 'src/utils/pos'; export function getQuoteActions( fyo: Fyo, @@ -914,7 +915,7 @@ export async function getPricingRule( continue; } - const filtered = filterPricingRules( + const filtered = await filterPricingRules( pricingRuleDocsForItem, doc.date as Date, item.quantity as number, @@ -977,16 +978,31 @@ export async function getItemRateFromPriceList( return plItem?.rate; } -export function filterPricingRules( +export async function filterPricingRules( pricingRuleDocsForItem: PricingRule[], sinvDate: Date, quantity: number, amount: Money -): PricingRule[] | [] { - const filteredPricingRules: PricingRule[] | undefined = []; +): Promise { + const filteredPricingRules: PricingRule[] = []; for (const pricingRuleDoc of pricingRuleDocsForItem) { - if (canApplyPricingRule(pricingRuleDoc, sinvDate, quantity, amount)) { + let freeItemQty: number | undefined; + + if (pricingRuleDoc?.freeItem) { + const itemQtyMap = await getItemQtyMap(); + freeItemQty = itemQtyMap[pricingRuleDoc.freeItem].availableQty; + } + + if ( + canApplyPricingRule( + pricingRuleDoc, + sinvDate, + quantity, + amount, + freeItemQty ?? 0 + ) + ) { filteredPricingRules.push(pricingRuleDoc); } } @@ -997,9 +1013,19 @@ export function canApplyPricingRule( pricingRuleDoc: PricingRule, sinvDate: Date, quantity: number, - amount: Money + amount: Money, + freeItemQty: number ): boolean { // Filter by Quantity + if ( + pricingRuleDoc.freeItem && + pricingRuleDoc.freeItemQuantity! >= freeItemQty + ) { + throw new ValidationError( + t`Free item '${pricingRuleDoc.freeItem}' does not have a specified quantity` + ); + } + if ( (pricingRuleDoc.minQuantity as number) > 0 && quantity < (pricingRuleDoc.minQuantity as number)