2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

fix: pricingRule validation

This commit is contained in:
akshayitzme 2024-09-09 09:38:02 +05:30
parent 733f60247d
commit 9741c54458
3 changed files with 99 additions and 85 deletions

View File

@ -1227,19 +1227,21 @@ export abstract class Invoice extends Transactional {
continue; continue;
} }
const isPricingRuleHasConflicts = getPricingRulesConflicts( for (const filteredDoc of filtered) {
filtered, const isPricingRuleHasConflicts = await getPricingRulesConflicts(
filtered[0].priority as number filteredDoc,
); filteredDoc.priority as number
);
if (isPricingRuleHasConflicts) { if (isPricingRuleHasConflicts) {
continue; 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;

View File

@ -14,7 +14,6 @@ import {
import { DocValue } from 'fyo/core/types'; import { DocValue } from 'fyo/core/types';
import { ValidationError } from 'fyo/utils/errors'; import { ValidationError } from 'fyo/utils/errors';
import { t } from 'fyo'; import { t } from 'fyo';
import { ModelNameEnum } from 'models/types';
export class PricingRule extends Doc { export class PricingRule extends Doc {
isEnabled?: boolean; isEnabled?: boolean;
@ -130,46 +129,18 @@ export class PricingRule extends Doc {
} }
}, },
priority: async (value: DocValue) => { 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) { if (!pricingRuleConflicts) {
const duplicatePricingRuleItems = (await this.fyo.db.getAll( return;
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}.`
);
}
} }
throw new ValidationError(
t`Pricing Rules ${pricingRuleConflicts.pricingRule} has the same Priority for the Item ${pricingRuleConflicts.item}.`
);
}, },
}; };

View File

@ -24,6 +24,7 @@ import { InvoiceStatus, ModelNameEnum } from './types';
import { Lead } from './baseModels/Lead/Lead'; import { Lead } from './baseModels/Lead/Lead';
import { PricingRule } from './baseModels/PricingRule/PricingRule'; import { PricingRule } from './baseModels/PricingRule/PricingRule';
import { ApplicablePricingRules } from './baseModels/Invoice/types'; import { ApplicablePricingRules } from './baseModels/Invoice/types';
import { PricingRuleItem } from './baseModels/PricingRuleItem/PricingRuleItem';
export function getQuoteActions( export function getQuoteActions(
fyo: Fyo, fyo: Fyo,
@ -662,13 +663,13 @@ export async function addItem<M extends ModelsWithItems>(name: string, doc: M) {
export async function getPricingRule( export async function getPricingRule(
doc: Invoice doc: Invoice
): Promise<ApplicablePricingRules[] | null> { ): Promise<ApplicablePricingRules[] | undefined> {
if ( if (
!doc.fyo.singles.AccountingSettings?.enablePricingRule || !doc.fyo.singles.AccountingSettings?.enablePricingRule ||
!doc.isSales || !doc.isSales ||
!doc.items !doc.items
) { ) {
return null; return;
} }
const pricingRules: ApplicablePricingRules[] = []; const pricingRules: ApplicablePricingRules[] = [];
@ -712,22 +713,23 @@ export async function getPricingRule(
continue; continue;
} }
const isPricingRuleHasConflicts = getPricingRulesConflicts( for (const filteredPricingRule of filtered) {
filtered, const isPricingRuleHasConflicts = await getPricingRulesConflicts(
filtered[0].priority as number filteredPricingRule,
); filteredPricingRule.priority as number
);
if (isPricingRuleHasConflicts) { if (isPricingRuleHasConflicts) {
continue; continue;
}
pricingRules.push({
applyOnItem: item.item as string,
pricingRule: filtered[0],
});
} }
return pricingRules;
pricingRules.push({
applyOnItem: item.item as string,
pricingRule: filtered[0],
});
} }
return pricingRules;
} }
export function filterPricingRules( export function filterPricingRules(
@ -800,32 +802,71 @@ export function canApplyPricingRule(
return true; return true;
} }
export function getPricingRulesConflicts( export async function getPricingRulesConflicts(
pricingRules: PricingRule[], pricingRule: PricingRule,
priority: number priority: number
): string[] | undefined { ): Promise<{ item: string; pricingRule: string } | undefined> {
const pricingRuleDocs = Array.from(pricingRules); const items = pricingRule.appliedItems?.map((item) => item.item) as string[];
const firstPricingRule = pricingRuleDocs.shift(); for (const item of items) {
if (!priority) { const duplicatePricingRuleItems = (
return; 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)) { const pricingRuleDocs = (await pricingRule.fyo.db.getAll(
if (pricingRuleDoc.priority !== firstPricingRule?.priority) { ModelNameEnum.PricingRule,
continue; {
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( export function roundFreeItemQty(