From 9741c54458fe9327441bb0c610d49bc04290496e Mon Sep 17 00:00:00 2001 From: akshayitzme Date: Mon, 9 Sep 2024 09:38:02 +0530 Subject: [PATCH] fix: pricingRule validation --- models/baseModels/Invoice/Invoice.ts | 24 ++-- models/baseModels/PricingRule/PricingRule.ts | 49 ++------ models/helpers.ts | 111 +++++++++++++------ 3 files changed, 99 insertions(+), 85 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 4b28f4a8..1560050d 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -1227,19 +1227,21 @@ export abstract class Invoice extends Transactional { continue; } - const isPricingRuleHasConflicts = getPricingRulesConflicts( - filtered, - filtered[0].priority as number - ); + for (const filteredDoc of filtered) { + const isPricingRuleHasConflicts = await getPricingRulesConflicts( + filteredDoc, + filteredDoc.priority as number + ); - if (isPricingRuleHasConflicts) { - continue; + if (isPricingRuleHasConflicts) { + continue; + } + + pricingRules.push({ + applyOnItem: item.item as string, + pricingRule: filtered[0], + }); } - - pricingRules.push({ - applyOnItem: item.item as string, - pricingRule: filtered[0], - }); } return pricingRules; diff --git a/models/baseModels/PricingRule/PricingRule.ts b/models/baseModels/PricingRule/PricingRule.ts index 16885dbb..bb0adbed 100644 --- a/models/baseModels/PricingRule/PricingRule.ts +++ b/models/baseModels/PricingRule/PricingRule.ts @@ -14,7 +14,6 @@ import { import { DocValue } from 'fyo/core/types'; import { ValidationError } from 'fyo/utils/errors'; import { t } from 'fyo'; -import { ModelNameEnum } from 'models/types'; export class PricingRule extends Doc { isEnabled?: boolean; @@ -130,46 +129,18 @@ export class PricingRule extends Doc { } }, priority: async (value: DocValue) => { - const items = this.appliedItems?.map((item) => item.item) as string[]; + const pricingRuleConflicts = await getPricingRulesConflicts( + this, + value as number + ); - for (const item of items) { - const duplicatePricingRuleItems = (await this.fyo.db.getAll( - ModelNameEnum.PricingRuleItem, - { - fields: ['parent'], - filters: { - item: item, - }, - } - )) as PricingRuleItem[]; - - const pricingRules = duplicatePricingRuleItems.map( - (item) => item.parent - ); - - const pricingRuleItems = (await this.fyo.db.getAll( - ModelNameEnum.PricingRule, - { - fields: ['*'], - filters: { - name: ['in', pricingRules as string[]], - }, - } - )) as PricingRule[]; - - const isPricingRuleHasConflicts = getPricingRulesConflicts( - pricingRuleItems, - value as number - ); - - if (isPricingRuleHasConflicts) { - throw new ValidationError( - t`Pricing Rules ${isPricingRuleHasConflicts.join( - ', ' - )} has the same Priority for the Item ${item}.` - ); - } + if (!pricingRuleConflicts) { + return; } + + throw new ValidationError( + t`Pricing Rules ${pricingRuleConflicts.pricingRule} has the same Priority for the Item ${pricingRuleConflicts.item}.` + ); }, }; diff --git a/models/helpers.ts b/models/helpers.ts index 621cf32f..0ad718d7 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -24,6 +24,7 @@ import { InvoiceStatus, ModelNameEnum } from './types'; import { Lead } from './baseModels/Lead/Lead'; import { PricingRule } from './baseModels/PricingRule/PricingRule'; import { ApplicablePricingRules } from './baseModels/Invoice/types'; +import { PricingRuleItem } from './baseModels/PricingRuleItem/PricingRuleItem'; export function getQuoteActions( fyo: Fyo, @@ -662,13 +663,13 @@ export async function addItem(name: string, doc: M) { export async function getPricingRule( doc: Invoice -): Promise { +): Promise { if ( !doc.fyo.singles.AccountingSettings?.enablePricingRule || !doc.isSales || !doc.items ) { - return null; + return; } const pricingRules: ApplicablePricingRules[] = []; @@ -712,22 +713,23 @@ export async function getPricingRule( continue; } - const isPricingRuleHasConflicts = getPricingRulesConflicts( - filtered, - filtered[0].priority as number - ); + for (const filteredPricingRule of filtered) { + const isPricingRuleHasConflicts = await getPricingRulesConflicts( + filteredPricingRule, + filteredPricingRule.priority as number + ); - if (isPricingRuleHasConflicts) { - continue; + if (isPricingRuleHasConflicts) { + continue; + } + + pricingRules.push({ + applyOnItem: item.item as string, + pricingRule: filtered[0], + }); } - - pricingRules.push({ - applyOnItem: item.item as string, - pricingRule: filtered[0], - }); + return pricingRules; } - - return pricingRules; } export function filterPricingRules( @@ -800,32 +802,71 @@ export function canApplyPricingRule( return true; } -export function getPricingRulesConflicts( - pricingRules: PricingRule[], +export async function getPricingRulesConflicts( + pricingRule: PricingRule, priority: number -): string[] | undefined { - const pricingRuleDocs = Array.from(pricingRules); +): Promise<{ item: string; pricingRule: string } | undefined> { + const items = pricingRule.appliedItems?.map((item) => item.item) as string[]; - const firstPricingRule = pricingRuleDocs.shift(); - if (!priority) { - return; - } + for (const item of items) { + const duplicatePricingRuleItems = ( + await pricingRule.fyo.db.getAll(ModelNameEnum.PricingRuleItem, { + fields: ['parent'], + filters: { + item: item, + }, + }) + ).filter( + (doc) => doc.parent !== (pricingRule.name as string) + ) as PricingRuleItem[]; - const conflictingPricingRuleNames: string[] = []; + const pricingRuleNames = duplicatePricingRuleItems.map( + (item) => item.parent + ) as string[]; - for (const pricingRuleDoc of pricingRuleDocs.slice(0)) { - if (pricingRuleDoc.priority !== firstPricingRule?.priority) { - continue; + const pricingRuleDocs = (await pricingRule.fyo.db.getAll( + ModelNameEnum.PricingRule, + { + fields: ['*'], + filters: { + name: ['in', pricingRuleNames], + }, + } + )) as PricingRule[]; + + for (const pricingRuleDoc of pricingRuleDocs) { + const minQtys = [ + pricingRuleDoc.minQuantity, + pricingRule.minQuantity, + ].sort() as number[]; + + const maxQtys = [ + pricingRuleDoc.maxQuantity, + pricingRule.maxQuantity, + ].sort() as number[]; + + const qtyHasConflicts = minQtys[1] <= maxQtys[0]; + + const minAmounts = [ + pricingRuleDoc.minAmount?.float, + pricingRule.minAmount?.float, + ].sort() as number[]; + + const maxAmounts = [ + pricingRuleDoc.maxAmount?.float, + pricingRule.maxAmount?.float, + ].sort() as number[]; + + const amountHasConflicts = minAmounts[1] <= maxAmounts[0]; + + if ( + (amountHasConflicts || qtyHasConflicts) && + pricingRuleDoc.priority === priority + ) { + return { pricingRule: pricingRuleDoc.name as string, item: item }; + } } - - conflictingPricingRuleNames.push(pricingRuleDoc.name as string); } - - if (!conflictingPricingRuleNames.length) { - return; - } - - return conflictingPricingRuleNames; } export function roundFreeItemQty(