mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
fix: batch-wise stock validations
This commit is contained in:
parent
20279c5a12
commit
a8d5d9d7ef
@ -82,12 +82,13 @@ export class StockManager {
|
||||
#getSMIDetails(transferDetails: SMTransferDetails): SMIDetails {
|
||||
return Object.assign({}, this.details, transferDetails);
|
||||
}
|
||||
// flag
|
||||
|
||||
async #validate(details: SMIDetails) {
|
||||
this.#validateRate(details);
|
||||
this.#validateQuantity(details);
|
||||
this.#validateLocation(details);
|
||||
await this.#validateStockAvailability(details);
|
||||
await this.#validateBatchWiseStockAvailability(details);
|
||||
}
|
||||
|
||||
#validateQuantity(details: SMIDetails) {
|
||||
@ -125,7 +126,59 @@ export class StockManager {
|
||||
|
||||
throw new ValidationError(t`Both From and To Location cannot be undefined`);
|
||||
}
|
||||
// flag
|
||||
|
||||
async #validateBatchWiseStockAvailability(details: SMIDetails) {
|
||||
/*
|
||||
* Checks if hasBatchNumber is enabled in the item
|
||||
* If user has not entered batchNumber raises a ValidationError
|
||||
* If entered quantity is greater than the available quantity in the batch, raises a ValidationError
|
||||
*/
|
||||
|
||||
if (!details.fromLocation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ifItemHasBatchNumber = await this.fyo.getValue(
|
||||
'Item',
|
||||
details.item,
|
||||
'hasBatchNumber'
|
||||
);
|
||||
const date = details.date.toISOString();
|
||||
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
||||
|
||||
if (ifItemHasBatchNumber) {
|
||||
if (!details.batchNumber) {
|
||||
throw new ValidationError(
|
||||
t`Please enter Batch Number for ${details.item}`
|
||||
);
|
||||
}
|
||||
|
||||
const itemsInBatch =
|
||||
(await this.fyo.db.getStockQuantity(
|
||||
details.item,
|
||||
details.fromLocation,
|
||||
undefined,
|
||||
date,
|
||||
details.batchNumber
|
||||
)) ?? 0;
|
||||
|
||||
if (details.quantity > itemsInBatch) {
|
||||
throw new ValidationError(
|
||||
[
|
||||
t`Insufficient Quantity in Batch ${details.batchNumber}`,
|
||||
t`Additional quantity (${
|
||||
details.quantity - itemsInBatch
|
||||
}) is required in batch ${
|
||||
details.batchNumber
|
||||
} to make the outward transfer of item ${details.item} from ${
|
||||
details.fromLocation
|
||||
} on ${formattedDate}`,
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #validateStockAvailability(details: SMIDetails) {
|
||||
if (!details.fromLocation) {
|
||||
return;
|
||||
@ -141,43 +194,20 @@ export class StockManager {
|
||||
undefined
|
||||
)) ?? 0;
|
||||
|
||||
|
||||
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
||||
|
||||
if (this.isCancelled) {
|
||||
quantityBefore += details.quantity;
|
||||
}
|
||||
|
||||
// batch-wise stock validation
|
||||
let itemsInBatch =
|
||||
(await this.fyo.db.getStockQuantity(
|
||||
details.item,
|
||||
details.fromLocation,
|
||||
undefined,
|
||||
date,
|
||||
details.batchNumber
|
||||
)) ?? 0;
|
||||
|
||||
|
||||
let ifItemHasBatchNumber= await this.fyo.getValue('Item', details.item, 'hasBatchNumber');
|
||||
|
||||
if ((ifItemHasBatchNumber && details.batchNumber) && details.quantity > itemsInBatch) {
|
||||
throw new ValidationError(
|
||||
[
|
||||
t`Insufficient Quantity in Batch ${details.batchNumber}`,
|
||||
t`Additional quantity (${details.quantity - itemsInBatch
|
||||
}) is required in batch ${details.batchNumber} to make the outward transfer of item ${details.item} from ${details.fromLocation
|
||||
} on ${formattedDate}`,
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
if (quantityBefore < details.quantity) {
|
||||
throw new ValidationError(
|
||||
[
|
||||
t`Insufficient Quantity.`,
|
||||
t`Additional quantity (${details.quantity - quantityBefore
|
||||
}) required to make outward transfer of item ${details.item} from ${details.fromLocation
|
||||
t`Additional quantity (${
|
||||
details.quantity - quantityBefore
|
||||
}) required to make outward transfer of item ${details.item} from ${
|
||||
details.fromLocation
|
||||
} on ${formattedDate}`,
|
||||
].join('\n')
|
||||
);
|
||||
@ -201,8 +231,10 @@ export class StockManager {
|
||||
[
|
||||
t`Insufficient Quantity.`,
|
||||
t`Transfer will cause future entries to have negative stock.`,
|
||||
t`Additional quantity (${quantityAfter - quantityRemaining
|
||||
}) required to make outward transfer of item ${details.item} from ${details.fromLocation
|
||||
t`Additional quantity (${
|
||||
quantityAfter - quantityRemaining
|
||||
}) required to make outward transfer of item ${details.item} from ${
|
||||
details.fromLocation
|
||||
} on ${formattedDate}`,
|
||||
].join('\n')
|
||||
);
|
||||
@ -210,8 +242,6 @@ export class StockManager {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class StockManagerItem {
|
||||
/**
|
||||
* The Stock Manager Item is used to move stock to and from a location. It
|
||||
|
@ -260,6 +260,11 @@ export class StockLedger extends Report {
|
||||
label: 'Location',
|
||||
fieldtype: 'Link',
|
||||
},
|
||||
{
|
||||
fieldname: 'batchNumber',
|
||||
label: 'Batch No.',
|
||||
fieldtype: 'Link',
|
||||
},
|
||||
{
|
||||
fieldname: 'quantity',
|
||||
label: 'Quantity',
|
||||
|
@ -17,7 +17,7 @@ export async function getRawStockLedgerEntries(fyo: Fyo) {
|
||||
'name',
|
||||
'date',
|
||||
'item',
|
||||
"batchNumber",
|
||||
'batchNumber',
|
||||
'rate',
|
||||
'quantity',
|
||||
'location',
|
||||
@ -43,7 +43,14 @@ export function getStockLedgerEntries(
|
||||
const name = safeParseInt(sle.name);
|
||||
const date = new Date(sle.date);
|
||||
const rate = safeParseFloat(sle.rate);
|
||||
const { item, location, batchNumber, quantity, referenceName, referenceType } = sle;
|
||||
const {
|
||||
item,
|
||||
location,
|
||||
batchNumber,
|
||||
quantity,
|
||||
referenceName,
|
||||
referenceType,
|
||||
} = sle;
|
||||
|
||||
if (quantity === 0) {
|
||||
continue;
|
||||
@ -126,7 +133,11 @@ export function getStockBalanceEntries(
|
||||
}
|
||||
|
||||
sbeMap[sle.item] ??= {};
|
||||
sbeMap[sle.item][sle.location] ??= getSBE(sle.item, sle.location);
|
||||
sbeMap[sle.item][sle.location] ??= getSBE(
|
||||
sle.item,
|
||||
sle.location,
|
||||
sle.batchNumber
|
||||
);
|
||||
const date = sle.date.valueOf();
|
||||
|
||||
if (fromDate && date < fromDate) {
|
||||
@ -148,13 +159,18 @@ export function getStockBalanceEntries(
|
||||
.flat();
|
||||
}
|
||||
|
||||
function getSBE(item: string, location: string): StockBalanceEntry {
|
||||
function getSBE(
|
||||
item: string,
|
||||
location: string,
|
||||
batchNumber: string
|
||||
): StockBalanceEntry {
|
||||
return {
|
||||
name: 0,
|
||||
|
||||
item,
|
||||
location,
|
||||
|
||||
batchNumber,
|
||||
balanceQuantity: 0,
|
||||
balanceValue: 0,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user