mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +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 {
|
#getSMIDetails(transferDetails: SMTransferDetails): SMIDetails {
|
||||||
return Object.assign({}, this.details, transferDetails);
|
return Object.assign({}, this.details, transferDetails);
|
||||||
}
|
}
|
||||||
// flag
|
|
||||||
async #validate(details: SMIDetails) {
|
async #validate(details: SMIDetails) {
|
||||||
this.#validateRate(details);
|
this.#validateRate(details);
|
||||||
this.#validateQuantity(details);
|
this.#validateQuantity(details);
|
||||||
this.#validateLocation(details);
|
this.#validateLocation(details);
|
||||||
await this.#validateStockAvailability(details);
|
await this.#validateStockAvailability(details);
|
||||||
|
await this.#validateBatchWiseStockAvailability(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
#validateQuantity(details: SMIDetails) {
|
#validateQuantity(details: SMIDetails) {
|
||||||
@ -125,7 +126,59 @@ export class StockManager {
|
|||||||
|
|
||||||
throw new ValidationError(t`Both From and To Location cannot be undefined`);
|
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) {
|
async #validateStockAvailability(details: SMIDetails) {
|
||||||
if (!details.fromLocation) {
|
if (!details.fromLocation) {
|
||||||
return;
|
return;
|
||||||
@ -141,44 +194,21 @@ export class StockManager {
|
|||||||
undefined
|
undefined
|
||||||
)) ?? 0;
|
)) ?? 0;
|
||||||
|
|
||||||
|
|
||||||
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
||||||
|
|
||||||
if (this.isCancelled) {
|
if (this.isCancelled) {
|
||||||
quantityBefore += details.quantity;
|
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) {
|
if (quantityBefore < details.quantity) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
[
|
[
|
||||||
t`Insufficient Quantity.`,
|
t`Insufficient Quantity.`,
|
||||||
t`Additional quantity (${details.quantity - quantityBefore
|
t`Additional quantity (${
|
||||||
}) required to make outward transfer of item ${details.item} from ${details.fromLocation
|
details.quantity - quantityBefore
|
||||||
} on ${formattedDate}`,
|
}) required to make outward transfer of item ${details.item} from ${
|
||||||
|
details.fromLocation
|
||||||
|
} on ${formattedDate}`,
|
||||||
].join('\n')
|
].join('\n')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -201,17 +231,17 @@ export class StockManager {
|
|||||||
[
|
[
|
||||||
t`Insufficient Quantity.`,
|
t`Insufficient Quantity.`,
|
||||||
t`Transfer will cause future entries to have negative stock.`,
|
t`Transfer will cause future entries to have negative stock.`,
|
||||||
t`Additional quantity (${quantityAfter - quantityRemaining
|
t`Additional quantity (${
|
||||||
}) required to make outward transfer of item ${details.item} from ${details.fromLocation
|
quantityAfter - quantityRemaining
|
||||||
} on ${formattedDate}`,
|
}) required to make outward transfer of item ${details.item} from ${
|
||||||
|
details.fromLocation
|
||||||
|
} on ${formattedDate}`,
|
||||||
].join('\n')
|
].join('\n')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
class StockManagerItem {
|
class StockManagerItem {
|
||||||
/**
|
/**
|
||||||
* The Stock Manager Item is used to move stock to and from a location. It
|
* 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',
|
label: 'Location',
|
||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: 'batchNumber',
|
||||||
|
label: 'Batch No.',
|
||||||
|
fieldtype: 'Link',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'quantity',
|
fieldname: 'quantity',
|
||||||
label: 'Quantity',
|
label: 'Quantity',
|
||||||
|
@ -17,7 +17,7 @@ export async function getRawStockLedgerEntries(fyo: Fyo) {
|
|||||||
'name',
|
'name',
|
||||||
'date',
|
'date',
|
||||||
'item',
|
'item',
|
||||||
"batchNumber",
|
'batchNumber',
|
||||||
'rate',
|
'rate',
|
||||||
'quantity',
|
'quantity',
|
||||||
'location',
|
'location',
|
||||||
@ -43,7 +43,14 @@ export function getStockLedgerEntries(
|
|||||||
const name = safeParseInt(sle.name);
|
const name = safeParseInt(sle.name);
|
||||||
const date = new Date(sle.date);
|
const date = new Date(sle.date);
|
||||||
const rate = safeParseFloat(sle.rate);
|
const rate = safeParseFloat(sle.rate);
|
||||||
const { item, location, batchNumber, quantity, referenceName, referenceType } = sle;
|
const {
|
||||||
|
item,
|
||||||
|
location,
|
||||||
|
batchNumber,
|
||||||
|
quantity,
|
||||||
|
referenceName,
|
||||||
|
referenceType,
|
||||||
|
} = sle;
|
||||||
|
|
||||||
if (quantity === 0) {
|
if (quantity === 0) {
|
||||||
continue;
|
continue;
|
||||||
@ -126,7 +133,11 @@ export function getStockBalanceEntries(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sbeMap[sle.item] ??= {};
|
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();
|
const date = sle.date.valueOf();
|
||||||
|
|
||||||
if (fromDate && date < fromDate) {
|
if (fromDate && date < fromDate) {
|
||||||
@ -148,13 +159,18 @@ export function getStockBalanceEntries(
|
|||||||
.flat();
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSBE(item: string, location: string): StockBalanceEntry {
|
function getSBE(
|
||||||
|
item: string,
|
||||||
|
location: string,
|
||||||
|
batchNumber: string
|
||||||
|
): StockBalanceEntry {
|
||||||
return {
|
return {
|
||||||
name: 0,
|
name: 0,
|
||||||
|
|
||||||
item,
|
item,
|
||||||
location,
|
location,
|
||||||
|
|
||||||
|
batchNumber,
|
||||||
balanceQuantity: 0,
|
balanceQuantity: 0,
|
||||||
balanceValue: 0,
|
balanceValue: 0,
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user