2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 15:20:56 +00:00

feat: BatchNumber schema & batch-wise validation

This commit is contained in:
akshayitzme 2023-01-14 16:58:16 +05:30
parent 08e46592a9
commit 8a4e1e4f41
12 changed files with 95 additions and 22 deletions

View File

@ -137,7 +137,8 @@ export class BespokeQueries {
item: string,
location?: string,
fromDate?: string,
toDate?: string
toDate?: string,
batchNumber?: string
): Promise<number | null> {
const query = db.knex!(ModelNameEnum.StockLedgerEntry)
.sum('quantity')
@ -147,6 +148,10 @@ export class BespokeQueries {
query.andWhere('location', location);
}
if (batchNumber) {
query.andWhere('batchNumber', batchNumber);
}
if (fromDate) {
query.andWhereRaw('datetime(date) > datetime(?)', [fromDate]);
}
@ -163,3 +168,5 @@ export class BespokeQueries {
return value[0][Object.keys(value[0])[0]];
}
}
//

View File

@ -312,14 +312,16 @@ export class DatabaseHandler extends DatabaseBase {
item: string,
location?: string,
fromDate?: string,
toDate?: string
toDate?: string,
batchNumber?: string
): Promise<number | null> {
return (await this.#demux.callBespoke(
'getStockQuantity',
item,
location,
fromDate,
toDate
toDate,
batchNumber
)) as number | null;
}

View File

@ -0,0 +1,13 @@
import { Doc } from 'fyo/model/doc';
import {
ListViewSettings,
} from 'fyo/model/types';
export class BatchNumber extends Doc {
static getListViewSettings(): ListViewSettings {
return {
columns: ["name", "expiryDate", "manufactureDate"],
};
}
}

View File

@ -19,7 +19,6 @@ export class Item extends Doc {
itemType?: 'Product' | 'Service';
for?: 'Purchases' | 'Sales' | 'Both';
hasBatchNumber?: boolean;
batchNumber?:string;
formulas: FormulaMap = {
incomeAccount: {
@ -122,6 +121,7 @@ export class Item extends Doc {
!this.fyo.singles.AccountingSettings?.enableInventory ||
this.itemType !== 'Product' ||
(this.inserted && !this.trackItem),
batchNumber: () => (!this.hasBatchNumber),
};
readOnly: ReadOnlyMap = {

View File

@ -26,12 +26,14 @@ import { ShipmentItem } from './inventory/ShipmentItem';
import { StockLedgerEntry } from './inventory/StockLedgerEntry';
import { StockMovement } from './inventory/StockMovement';
import { StockMovementItem } from './inventory/StockMovementItem';
import { BatchNumber } from './baseModels/BatchNumber/BatchNumber';
export const models = {
Account,
AccountingLedgerEntry,
AccountingSettings,
Address,
BatchNumber,
Defaults,
Item,
JournalEntry,

View File

@ -82,7 +82,7 @@ export class StockManager {
#getSMIDetails(transferDetails: SMTransferDetails): SMIDetails {
return Object.assign({}, this.details, transferDetails);
}
// flag
async #validate(details: SMIDetails) {
this.#validateRate(details);
this.#validateQuantity(details);
@ -125,7 +125,7 @@ export class StockManager {
throw new ValidationError(t`Both From and To Location cannot be undefined`);
}
// flag
async #validateStockAvailability(details: SMIDetails) {
if (!details.fromLocation) {
return;
@ -137,8 +137,11 @@ export class StockManager {
details.item,
details.fromLocation,
undefined,
date
date,
details.batchNumber
)) ?? 0;
console.log(quantityBefore);
const formattedDate = this.fyo.format(details.date, 'Datetime');
@ -150,10 +153,8 @@ export class StockManager {
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')
);
@ -162,7 +163,9 @@ export class StockManager {
const quantityAfter = await this.fyo.db.getStockQuantity(
details.item,
details.fromLocation,
details.date.toISOString()
details.date.toISOString(),
undefined,
details.batchNumber
);
if (quantityAfter === null) {
// No future transactions
@ -175,10 +178,8 @@ 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')
);
@ -186,6 +187,8 @@ export class StockManager {
}
}
//
class StockManagerItem {
/**
* The Stock Manager Item is used to move stock to and from a location. It

View File

@ -19,7 +19,7 @@ export class StockMovementItem extends Doc {
rate?: Money;
amount?: Money;
parentdoc?: StockMovement;
batchNumber? : string;
batchNumber?: string;
get isIssue() {
return this.parentdoc?.movementType === MovementType.MaterialIssue;

View File

@ -0,0 +1,29 @@
{
"name": "BatchNumber",
"label": "Batch Number",
"naming": "manual",
"fields": [
{
"fieldname": "name",
"fieldtype": "Data",
"create": true,
"label": "Batch Number",
"required": true
},
{
"fieldname": "expiryDate",
"fieldtype": "Date",
"label": "Expiry Date",
"create": true,
"required": false
},
{
"fieldname": "manufactureDate",
"fieldtype": "Date",
"label": "Manufacture Date",
"create": true,
"required": false
}
],
"quickEditFields": ["batchNumber", "expiryDate", "manufactureDate"]
}

View File

@ -97,8 +97,9 @@
{
"fieldname": "batchNumber",
"label": "Batch No",
"fieldtype": "Data",
"fieldtype": "Link",
"create": true,
"target": "BatchNumber",
"placeholder": "Batch No"
}
],

View File

@ -26,6 +26,13 @@
"target": "Location",
"create": true
},
{
"fieldname": "batchNumber",
"label": "Batch No",
"fieldtype": "Link",
"target": "BatchNumber",
"create": true
},
{
"fieldname": "quantity",
"label": "Quantity",
@ -46,12 +53,13 @@
"readOnly": true
}
],
"tableFields": ["item", "fromLocation", "toLocation", "quantity", "rate"],
"keywordFields": ["item"],
"tableFields": ["item", "fromLocation", "toLocation", "batchNumber", "quantity", "rate"],
"keywordFields": ["item", "batchNumber"],
"quickEditFields": [
"item",
"fromLocation",
"toLocation",
"batchNumber",
"quantity",
"rate",
"amount"

View File

@ -46,6 +46,7 @@ import submittable from './meta/submittable.json';
import tree from './meta/tree.json';
import { Schema, SchemaStub } from './types';
import InventorySettings from './app/inventory/InventorySettings.json';
import BatchNumber from './app/BatchNumber.json'
export const coreSchemas: Schema[] = [
PatchRun as Schema,
@ -112,4 +113,6 @@ export const appSchemas: Schema[] | SchemaStub[] = [
ShipmentItem as Schema,
PurchaseReceipt as Schema,
PurchaseReceiptItem as Schema,
BatchNumber as Schema
];

View File

@ -96,6 +96,11 @@ async function getInventorySidebar(): Promise<SidebarRoot[]> {
name: 'stock-balance',
route: '/report/StockBalance',
},
{
label: t`Batch`,
name: 'batch-number',
route: '/list/BatchNumber',
}
],
},
];