mirror of
https://github.com/frappe/books.git
synced 2026-08-24 02:24:17 -05:00
Merge pull request #1304 from frappe/fix-batch-return
fix: partial returns for batched items
This commit is contained in:
@@ -672,7 +672,10 @@ export abstract class Invoice extends Transactional {
|
||||
|
||||
let returnDocItems: DocValueMap[] = [];
|
||||
|
||||
const totalQtyOfReturnedItems = await getReturnQtyTotal(this);
|
||||
const totalQtyOfReturnedItems: Record<
|
||||
string,
|
||||
number | { quantity?: number; batches?: Record<string, number> }
|
||||
> = await getReturnQtyTotal(this);
|
||||
|
||||
const returnBalanceItemsQty = await this.fyo.db.getReturnBalanceItemsQty(
|
||||
this.schemaName,
|
||||
@@ -680,19 +683,34 @@ export abstract class Invoice extends Transactional {
|
||||
);
|
||||
|
||||
for (const item of docItems) {
|
||||
const itemName = item.item as string;
|
||||
|
||||
let balQuantity: number;
|
||||
if (!returnBalanceItemsQty) {
|
||||
returnDocItems = docItems.map((docItem) => ({
|
||||
if (item.batch) {
|
||||
const returnData = totalQtyOfReturnedItems[item.item as string];
|
||||
|
||||
if (typeof returnData === 'object' && returnData?.batches) {
|
||||
balQuantity = -returnData.batches[item.batch as string] || 0;
|
||||
}
|
||||
} else {
|
||||
balQuantity = -(totalQtyOfReturnedItems[itemName] || 0);
|
||||
}
|
||||
|
||||
const returnDocs = docItems.map((docItem) => ({
|
||||
...docItem,
|
||||
name: undefined,
|
||||
quantity: -(totalQtyOfReturnedItems[docItem.item as string] || 0),
|
||||
quantity: balQuantity,
|
||||
}));
|
||||
|
||||
returnDocItems.push(returnDocs[0]);
|
||||
|
||||
for (const row of returnDocItems) {
|
||||
row.itemDiscountedTotal = await this.getItemsDiscountedTotal(
|
||||
row as InvoiceItem
|
||||
);
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
const isItemExist = !!returnDocItems.filter(
|
||||
@@ -711,7 +729,7 @@ export abstract class Invoice extends Transactional {
|
||||
}
|
||||
|
||||
let quantity = returnedItem.quantity;
|
||||
const transferQuantity = quantity / (item.unitConversionFactor as number);
|
||||
let transferQuantity = quantity / (item.unitConversionFactor as number);
|
||||
|
||||
let serialNumber: string | undefined =
|
||||
returnedItem.serialNumbers?.join('\n');
|
||||
@@ -721,14 +739,20 @@ export abstract class Invoice extends Transactional {
|
||||
returnedItem.batches &&
|
||||
returnedItem.batches[item.batch as string]
|
||||
) {
|
||||
quantity = returnedItem.batches[item.batch as string].quantity;
|
||||
|
||||
if (returnedItem.batches[item.batch as string].serialNumbers) {
|
||||
serialNumber =
|
||||
returnedItem.batches[item.batch as string].serialNumbers?.join(
|
||||
'\n'
|
||||
);
|
||||
}
|
||||
const returnedItemsData = totalQtyOfReturnedItems[itemName];
|
||||
if (
|
||||
typeof returnedItemsData === 'object' &&
|
||||
returnedItemsData?.batches
|
||||
) {
|
||||
quantity = -returnedItemsData?.batches?.[item.batch as string];
|
||||
transferQuantity = quantity / (item.unitConversionFactor as number);
|
||||
}
|
||||
}
|
||||
|
||||
returnDocItems.push({
|
||||
@@ -740,21 +764,10 @@ export abstract class Invoice extends Transactional {
|
||||
});
|
||||
}
|
||||
|
||||
returnDocItems.forEach((docItems) => {
|
||||
const itemName = docItems.item as string;
|
||||
if (itemName in totalQtyOfReturnedItems) {
|
||||
docItems.quantity = totalQtyOfReturnedItems[itemName];
|
||||
}
|
||||
});
|
||||
|
||||
returnDocItems = returnDocItems.filter(
|
||||
(docItems) => (docItems.quantity as number) > 0
|
||||
(docItems) => (docItems.quantity as number) < 0
|
||||
);
|
||||
|
||||
returnDocItems.forEach((docItems) => {
|
||||
docItems.quantity = -(docItems.quantity as number);
|
||||
});
|
||||
|
||||
const returnDocData = {
|
||||
...docData,
|
||||
name: undefined,
|
||||
@@ -892,29 +905,49 @@ export abstract class Invoice extends Transactional {
|
||||
async getLPAddedBaseGrandTotal() {
|
||||
const totalDiscount = this.getTotalDiscount();
|
||||
|
||||
let baseTotal = this.fyo.pesa(0);
|
||||
if (!this.taxes || !this.taxes.length) {
|
||||
let baseTotal: Money;
|
||||
|
||||
if (!this.taxes?.length) {
|
||||
baseTotal = (this.netTotal as Money).sub(totalDiscount);
|
||||
} else {
|
||||
baseTotal = this.taxes
|
||||
.map((doc) => doc.amount as Money)
|
||||
.reduce((a, b) => {
|
||||
return a.add(b.abs());
|
||||
}, (this.netTotal as Money).abs())
|
||||
.reduce((a, b) => a.add(b.abs()), (this.netTotal as Money).abs())
|
||||
.sub(totalDiscount);
|
||||
}
|
||||
if (!this.isReturn) {
|
||||
const totalLoyaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram as string,
|
||||
this.loyaltyPoints as number
|
||||
);
|
||||
|
||||
const totalLoyaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram as string,
|
||||
this.loyaltyPoints as number
|
||||
);
|
||||
|
||||
if (this.isReturn) {
|
||||
return this.grandTotal;
|
||||
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 returnRatio = baseTotal
|
||||
.abs()
|
||||
.div(originalInvoice.netTotal as Money);
|
||||
|
||||
const originalLoyaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
originalInvoice.loyaltyProgram as string,
|
||||
originalInvoice.loyaltyPoints as number
|
||||
);
|
||||
|
||||
const proportionalLoyaltyAmount = originalLoyaltyAmount.mul(returnRatio);
|
||||
|
||||
return baseTotal.abs().sub(proportionalLoyaltyAmount).neg();
|
||||
}
|
||||
|
||||
return baseTotal;
|
||||
}
|
||||
formulas: FormulaMap = {
|
||||
account: {
|
||||
|
||||
+59
-26
@@ -748,7 +748,12 @@ export async function addItem<M extends ModelsWithItems>(name: string, doc: M) {
|
||||
|
||||
export async function getReturnQtyTotal(
|
||||
doc: Invoice
|
||||
): Promise<Record<string, number>> {
|
||||
): Promise<
|
||||
Record<
|
||||
string,
|
||||
number | { quantity: number; batches?: Record<string, number> }
|
||||
>
|
||||
> {
|
||||
const returnDocs = await doc.fyo.db.getAll(doc.schemaName, {
|
||||
fields: ['*'],
|
||||
filters: {
|
||||
@@ -757,36 +762,64 @@ export async function getReturnQtyTotal(
|
||||
});
|
||||
|
||||
const returnedDocs = await Promise.all(
|
||||
returnDocs.map((docss) =>
|
||||
doc.fyo.doc.getDoc(doc.schemaName, docss.name as string)
|
||||
)
|
||||
returnDocs.map((d) => doc.fyo.doc.getDoc(doc.schemaName, d.name as string))
|
||||
);
|
||||
|
||||
const quantitySum: { [key: string]: number } = {};
|
||||
const quantitySum: Record<
|
||||
string,
|
||||
number | { quantity: number; batches?: Record<string, number> }
|
||||
> = {};
|
||||
|
||||
if ('items' in doc && Array.isArray(doc.items)) {
|
||||
doc.items.forEach((docItem) => {
|
||||
const itemName = docItem.item as string;
|
||||
if (itemName) {
|
||||
quantitySum[itemName] = (docItem.quantity as number) || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (const item of doc.items || []) {
|
||||
const itemName = item.item;
|
||||
const batch = item.batch;
|
||||
const qty = item.quantity as number;
|
||||
|
||||
if (!returnedDocs) {
|
||||
return quantitySum;
|
||||
}
|
||||
returnedDocs.forEach((returnedDoc) => {
|
||||
if (returnedDoc && returnedDoc.items) {
|
||||
(returnedDoc.items as InvoiceItem[]).forEach((item) => {
|
||||
const itemName = item.item;
|
||||
if (itemName && quantitySum.hasOwnProperty(itemName)) {
|
||||
quantitySum[itemName] =
|
||||
quantitySum[itemName] - Math.abs(item.quantity as number);
|
||||
}
|
||||
});
|
||||
if (!itemName) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
if (batch) {
|
||||
if (!quantitySum[itemName]) {
|
||||
quantitySum[itemName] = { quantity: qty, batches: { [batch]: qty } };
|
||||
} else {
|
||||
const entry = quantitySum[itemName] as {
|
||||
quantity: number;
|
||||
batches?: Record<string, number>;
|
||||
};
|
||||
entry.quantity += qty;
|
||||
entry.batches![batch] = (entry.batches![batch] || 0) + qty;
|
||||
}
|
||||
} else {
|
||||
quantitySum[itemName] = ((quantitySum[itemName] as number) || 0) + qty;
|
||||
}
|
||||
}
|
||||
|
||||
for (const returnedDoc of returnedDocs) {
|
||||
for (const item of (returnedDoc?.items as InvoiceItem[]) || []) {
|
||||
const itemName = item.item;
|
||||
const batch = item.batch;
|
||||
const qty = Math.abs(item.quantity!);
|
||||
|
||||
if (!itemName || !quantitySum[itemName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (batch && quantitySum[itemName]) {
|
||||
const entry = quantitySum[itemName] as {
|
||||
quantity: number;
|
||||
batches?: Record<string, number>;
|
||||
};
|
||||
entry.quantity -= qty;
|
||||
if (entry.batches?.[batch]) {
|
||||
entry.batches[batch] -= qty;
|
||||
}
|
||||
} else {
|
||||
quantitySum[itemName] = (quantitySum[itemName] as number) - qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quantitySum;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user