Merge pull request #1242 from frappe/feat-sync-pricing-rule

feat: sync pricing rule from ERPNext
This commit is contained in:
Able k Saju
2025-06-11 15:08:34 +05:30
committed by GitHub
3 changed files with 84 additions and 7 deletions
+21 -3
View File
@@ -337,7 +337,17 @@ export abstract class Invoice extends Transactional {
const tax = await this.getTax(item.tax);
for (const details of (tax.details ?? []) as TaxDetail[]) {
const amount = item.amount!;
let amount = item.amount!;
if (!this.discountAfterTax) {
const itemDiscountAmount = this.getItemDiscountAmount();
if (this.isReturn) {
amount = amount.add(itemDiscountAmount);
} else {
amount = amount.sub(itemDiscountAmount);
}
}
const taxItem: InvoiceTaxItem = {
details,
@@ -416,6 +426,11 @@ export abstract class Invoice extends Transactional {
const itemDiscountAmount = this.getItemDiscountAmount();
const invoiceDiscountAmount = this.getInvoiceDiscountAmount();
if (this.isReturn) {
return itemDiscountAmount.add(invoiceDiscountAmount).neg();
}
return itemDiscountAmount.add(invoiceDiscountAmount);
}
@@ -430,7 +445,7 @@ export abstract class Invoice extends Transactional {
.map((doc) => doc.amount as Money)
.reduce((a, b) => {
if (this.isReturn) {
return a.add(b.abs()).neg();
return a.abs().add(b.abs()).neg();
}
return a.add(b.abs());
@@ -473,7 +488,9 @@ export abstract class Invoice extends Transactional {
for (const item of this.items) {
if (item.setItemDiscountAmount) {
discountAmount = discountAmount.add(
item.itemDiscountAmount ?? this.fyo.pesa(0)
(item.itemDiscountAmount ?? this.fyo.pesa(0)).mul(
item.quantity as number
)
);
} else if (!this.discountAfterTax) {
if (this.isReturn) {
@@ -566,6 +583,7 @@ export abstract class Invoice extends Transactional {
}
const docData = this.getValidDict(true, true);
docData.pricingRuleDetail = [];
const docItems = docData.items as DocValueMap[];
if (!docItems) {
+60 -3
View File
@@ -10,6 +10,8 @@ import { StockMovementItem } from 'models/inventory/StockMovementItem';
import { getRandomString } from '../../utils';
import { ErrorLogEnum } from 'fyo/telemetry/types';
import { ValidationError } from 'fyo/utils/errors';
import { PricingRule } from 'models/baseModels/PricingRule/PricingRule';
import { PricingRuleItem } from 'models/baseModels/PricingRuleItem/PricingRuleItem';
export async function registerInstanceToERPNext(fyo: Fyo) {
if (!navigator.onLine) {
@@ -153,10 +155,12 @@ export async function syncDocumentsFromERPNext(fyo: Fyo) {
(doc.fbooksDocName as string) || (doc.name as string)
);
doc.name = doc.fbooksDocName ?? doc.name;
doc = checkDocDataTypes(fyo, doc) as DocValueMap;
await existingDoc.setMultiple(doc);
await performPreSync(fyo, doc);
await appendDocValues(existingDoc as DocValueMap, doc);
existingDoc._addDocToSyncQueue = false;
await existingDoc.sync();
@@ -175,7 +179,7 @@ export async function syncDocumentsFromERPNext(fyo: Fyo) {
token,
deviceID,
doc,
doc.name as string,
(doc.erpnextDocName as string) || (doc.name as string),
doc.name as string
);
continue;
@@ -213,7 +217,7 @@ export async function syncDocumentsFromERPNext(fyo: Fyo) {
token,
deviceID,
doc,
doc.name as string,
(doc.erpnextDocName as string) || (doc.name as string),
newDoc.name as string
);
} catch (error) {
@@ -231,6 +235,36 @@ async function appendDocValues(newDoc: DocValueMap, doc: DocValueMap) {
conversionFactor: uomDoc.conversionFactor,
});
}
case ModelNameEnum.PricingRule:
const itemSet = new Set<string>();
(newDoc as Doc).appliedItems = (
newDoc as PricingRule
).appliedItems?.filter((row: PricingRuleItem) => {
const key = `${row.item as string}::${row.unit as string}`;
if (itemSet.has(key)) {
return false;
}
itemSet.add(key);
return true;
});
const docItemSet = new Set<string>(
(doc as PricingRule).appliedItems?.map(
(row: PricingRuleItem) =>
`${row.item as string}::${row.unit as string}`
) || []
);
(newDoc as PricingRule).appliedItems = (
newDoc as PricingRule
).appliedItems?.filter((row: PricingRuleItem) =>
docItemSet.has(`${row.item as string}::${row.unit as string}`)
);
break;
}
}
@@ -573,8 +607,28 @@ function checkDocDataTypes(
doc: DocValueMap | Doc
): DocValueMap | Doc {
switch (doc.doctype) {
case ModelNameEnum.Item:
case ModelNameEnum.Item: {
const fields = ['rate'];
const updatedDoc = changeDocDataType(
fyo,
doc,
fields,
ModelNameEnum.Currency
);
return updatedDoc;
}
case ModelNameEnum.PricingRule: {
const fields = [
'minAmount',
'maxAmount',
'discountAmount',
'discountRate',
'freeItemRate',
];
const updatedDoc = changeDocDataType(
fyo,
doc,
@@ -582,6 +636,8 @@ function checkDocDataTypes(
ModelNameEnum.Currency
);
return updatedDoc;
}
default:
return doc;
}
@@ -591,6 +647,7 @@ function isValidSyncableDocName(doctype: string): boolean {
const syncableDocNames = [
ModelNameEnum.Item,
ModelNameEnum.Batch,
ModelNameEnum.PricingRule,
] as string[];
if (syncableDocNames.includes(doctype)) {
+3 -1
View File
@@ -56,7 +56,9 @@ export function getItemDiscounts(items: SalesInvoiceItem[]): Money {
for (const item of items) {
if (item.setItemDiscountAmount) {
if (!item.itemDiscountAmount?.isZero()) {
itemDiscounts = itemDiscounts.add(item.itemDiscountAmount as Money);
itemDiscounts = itemDiscounts.add(
(item.itemDiscountAmount as Money).mul(item.quantity as number)
);
}
} else {
if (item.amount && (item.itemDiscountPercent as number) > 1) {