From 5b1018a0788387efe70419441f6ea531ba903890 Mon Sep 17 00:00:00 2001 From: akshayitzme Date: Thu, 1 Feb 2024 17:04:52 +0530 Subject: [PATCH] feat: pricing rule --- models/helpers.ts | 100 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/models/helpers.ts b/models/helpers.ts index 77af800dc..8e0879a6e 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -16,7 +16,8 @@ import { StockMovement } from './inventory/StockMovement'; import { StockTransfer } from './inventory/StockTransfer'; import { InvoiceStatus, ModelNameEnum } from './types'; import { PricingRule } from './baseModels/PricingRule/PricingRule'; -import { showToast } from 'src/utils/interactive'; +import { ValidationError } from 'fyo/utils/errors'; +import { ApplicablePricingRules } from './baseModels/Invoice/types'; export function getQuoteActions( fyo: Fyo, @@ -565,6 +566,76 @@ export async function addItem(name: string, doc: M) { await item.set('item', name); } +export async function getPricingRule( + doc: Invoice +): Promise { + if ( + !doc.fyo.singles.AccountingSettings?.enablePricingRule || + !doc.isSales || + !doc.items + ) { + return; + } + + const pricingRules: ApplicablePricingRules[] = []; + + for (const item of doc.items) { + if (item.isFreeItem) { + continue; + } + + const pricingRuleDocNames = ( + await doc.fyo.db.getAll(ModelNameEnum.PricingRuleItem, { + fields: ['parent'], + filters: { + item: item.item as string, + unit: item.unit as string, + }, + }) + ).map((doc) => doc.parent) as string[]; + + const pricingRuleDocsForItem = (await doc.fyo.db.getAll( + ModelNameEnum.PricingRule, + { + fields: ['*'], + filters: { + name: ['in', pricingRuleDocNames], + isEnabled: true, + }, + orderBy: 'priority', + order: 'desc', + } + )) as PricingRule[]; + + const filtered = filterPricingRules( + pricingRuleDocsForItem, + doc.date as Date, + item.quantity as number, + item.amount as Money + ); + + if (!filtered.length) { + continue; + } + + const isPricingRuleHasConflicts = getPricingRulesConflicts( + filtered, + item.item as string + ); + + if (isPricingRuleHasConflicts) { + continue; + } + + pricingRules.push({ + applyOnItem: item.item as string, + pricingRule: filtered[0], + }); + } + + return pricingRules; +} + export function filterPricingRules( pricingRuleDocsForItem: PricingRule[], sinvDate: Date, @@ -637,7 +708,7 @@ export function canApplyPricingRule( export function getPricingRulesConflicts( pricingRules: PricingRule[], item: string -) { +): string[] | undefined { const pricingRuleDocs = Array.from(pricingRules); const firstPricingRule = pricingRuleDocs.shift(); @@ -647,22 +718,29 @@ export function getPricingRulesConflicts( const conflictingPricingRuleNames: string[] = []; for (const pricingRuleDoc of pricingRuleDocs.slice(0)) { - if (pricingRuleDoc.priority === firstPricingRule?.priority) { - conflictingPricingRuleNames.push(pricingRuleDoc.name as string); + if (pricingRuleDoc.priority !== firstPricingRule?.priority) { + continue; } + + conflictingPricingRuleNames.push(pricingRuleDoc.name as string); } if (!conflictingPricingRuleNames.length) { - return false; + return; } - showToast({ - type: 'error', - message: t`Pricing Rules ${ + throw new ValidationError( + t`Pricing Rules ${ firstPricingRule.name as string }, ${conflictingPricingRuleNames.join( ', ' - )} has the same Priority for the Item ${item}.`, - }); - return true; + )} has the same Priority for the Item ${item}.` + ); +} + +export function roundFreeItemQty( + quantity: number, + roundingMethod: 'round' | 'floor' | 'ceil' +): number { + return Math[roundingMethod](quantity); }