From 0a6f36869291b5d8af0818c63611f152f295464e Mon Sep 17 00:00:00 2001 From: AbleKSaju <126228406+AbleKSaju@users.noreply.github.com> Date: Mon, 28 Jul 2025 15:59:51 +0530 Subject: [PATCH 1/2] fix: correct loyalty points calculation for returned batch items --- models/baseModels/Invoice/Invoice.ts | 28 +++++++------------ .../baseModels/SalesInvoice/SalesInvoice.ts | 17 +++++++---- models/helpers.ts | 23 +++++++++++++++ 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 522e345d..b07f28c4 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -25,6 +25,7 @@ import { removeLoyaltyPoint, roundFreeItemQty, getReturnQtyTotal, + getReturnLoyaltyPoints, } from 'models/helpers'; import { StockTransfer } from 'models/inventory/StockTransfer'; import { validateBatch } from 'models/inventory/helpers'; @@ -922,29 +923,20 @@ export abstract class Invoice extends Transactional { this.loyaltyPoints as number ); - const result = baseTotal.sub(totalLoyaltyAmount); - return result; + return baseTotal.sub(totalLoyaltyAmount); } - if (this.isReturn) { - const originalInvoice = (await this.fyo.doc.getDoc( - this.schemaName, - this.returnAgainst - )) as Invoice; + const loyaltyAmount = await getReturnLoyaltyPoints(this); - const returnRatio = baseTotal - .abs() - .div(originalInvoice.netTotal as Money); + const totalAmount = baseTotal.abs().sub(loyaltyAmount); - const originalLoyaltyAmount = await getAddedLPWithGrandTotal( - this.fyo, - originalInvoice.loyaltyProgram as string, - originalInvoice.loyaltyPoints as number - ); + this.loyaltyPoints = loyaltyAmount; + if (totalAmount.isNegative()) { + this.loyaltyPoints = totalAmount.abs().float - Math.abs(loyaltyAmount); + return this.fyo.pesa(0); + } - const proportionalLoyaltyAmount = originalLoyaltyAmount.mul(returnRatio); - - return baseTotal.abs().sub(proportionalLoyaltyAmount).neg(); + return baseTotal.abs().sub(loyaltyAmount); } return baseTotal; diff --git a/models/baseModels/SalesInvoice/SalesInvoice.ts b/models/baseModels/SalesInvoice/SalesInvoice.ts index aa4dca8f..a2ae5e7e 100644 --- a/models/baseModels/SalesInvoice/SalesInvoice.ts +++ b/models/baseModels/SalesInvoice/SalesInvoice.ts @@ -5,6 +5,7 @@ import { ModelNameEnum } from 'models/types'; import { getAddedLPWithGrandTotal, getInvoiceActions, + getReturnLoyaltyPoints, getTransactionStatusColumn, } from '../../helpers'; import { Invoice } from '../Invoice/Invoice'; @@ -42,11 +43,17 @@ export class SalesInvoice extends Invoice { this.loyaltyProgram )) as LoyaltyProgram; - const totalAmount = await getAddedLPWithGrandTotal( - this.fyo, - this.loyaltyProgram as string, - this.loyaltyPoints as number - ); + let totalAmount; + + if (this.isReturn) { + totalAmount = this.fyo.pesa(await getReturnLoyaltyPoints(this)); + } else { + totalAmount = await getAddedLPWithGrandTotal( + this.fyo, + this.loyaltyProgram as string, + this.loyaltyPoints as number + ); + } await posting.debit( loyaltyProgramDoc.expenseAccount as string, diff --git a/models/helpers.ts b/models/helpers.ts index 1442a97b..cf48a47d 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -746,6 +746,29 @@ export async function addItem(name: string, doc: M) { await item.set('item', name); } +export async function getReturnLoyaltyPoints(doc: Invoice) { + const returnDocs = await doc.fyo.db.getAll(doc.schemaName, { + fields: ['*'], + filters: { + returnAgainst: doc.returnAgainst as string, + submitted: true, + }, + }); + + const totalLoyaltyPoints = returnDocs.reduce( + (sum, doc) => sum + Math.abs(doc.loyaltyPoints as number), + 0 + ); + + const loyaltyPoints = await doc.fyo.getValue( + ModelNameEnum.SalesInvoice, + doc.returnAgainst as string, + 'loyaltyPoints' + ); + + return Math.abs((loyaltyPoints as number) - Math.abs(totalLoyaltyPoints)); +} + export async function getReturnQtyTotal( doc: Invoice ): Promise< From 798660abb2cd230a9a8c5bc74bdd26260d3598e1 Mon Sep 17 00:00:00 2001 From: AbleKSaju <126228406+AbleKSaju@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:04:17 +0530 Subject: [PATCH 2/2] refactor: improve loyalty points handling for invoice returns --- models/baseModels/Invoice/Invoice.ts | 48 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index b07f28c4..6dcccfea 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -59,6 +59,13 @@ export type TaxDetail = { rate: number; }; +export type ReturnedItemData = + | number + | { + quantity?: number; + batches?: Record; + }; + export type InvoiceTaxItem = { details: TaxDetail; exchangeRate?: number; @@ -684,34 +691,30 @@ export abstract class Invoice extends Transactional { ); for (const item of docItems) { - const itemName = item.item as string; - - let balQuantity: number; - if (!returnBalanceItemsQty) { + if (totalQtyOfReturnedItems) { if (item.batch) { const returnData = totalQtyOfReturnedItems[item.item as string]; - if (typeof returnData === 'object' && returnData?.batches) { - balQuantity = -returnData.batches[item.batch as string] || 0; + returnDocItems = docItems.map((docItem) => ({ + ...docItem, + name: undefined, + quantity: -returnData?.batches![docItem.batch as string] || 0, + })); } } else { - balQuantity = -(totalQtyOfReturnedItems[itemName] || 0); + returnDocItems = docItems.map((docItem) => ({ + ...docItem, + name: undefined, + quantity: -(totalQtyOfReturnedItems[docItem.item as string] || 0), + })); } - const returnDocs = docItems.map((docItem) => ({ - ...docItem, - name: undefined, - quantity: balQuantity, - })); - - returnDocItems.push(returnDocs[0]); - for (const row of returnDocItems) { row.itemDiscountedTotal = await this.getItemsDiscountedTotal( row as InvoiceItem ); } - continue; + break; } const isItemExist = !!returnDocItems.filter( @@ -723,7 +726,7 @@ export abstract class Invoice extends Transactional { } const returnedItem: ReturnDocItem | undefined = - returnBalanceItemsQty[item.item as string]; + returnBalanceItemsQty![item.item as string]; if (!returnedItem) { continue; @@ -746,12 +749,15 @@ export abstract class Invoice extends Transactional { '\n' ); } - const returnedItemsData = totalQtyOfReturnedItems[itemName]; + const returnedItemsData = totalQtyOfReturnedItems[ + item.item as string + ] as ReturnedItemData; + if ( typeof returnedItemsData === 'object' && - returnedItemsData?.batches + returnedItemsData.batches ) { - quantity = -returnedItemsData?.batches?.[item.batch as string]; + quantity = -returnedItemsData?.batches[item.batch as string]; transferQuantity = quantity / (item.unitConversionFactor as number); } } @@ -925,6 +931,7 @@ export abstract class Invoice extends Transactional { return baseTotal.sub(totalLoyaltyAmount); } + if (this.isReturn) { const loyaltyAmount = await getReturnLoyaltyPoints(this); @@ -941,6 +948,7 @@ export abstract class Invoice extends Transactional { return baseTotal; } + formulas: FormulaMap = { account: { formula: async () => {