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:
parent
08e46592a9
commit
8a4e1e4f41
@ -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]];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
@ -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;
|
||||
}
|
||||
|
||||
|
13
models/baseModels/BatchNumber/BatchNumber.ts
Normal file
13
models/baseModels/BatchNumber/BatchNumber.ts
Normal 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"],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
29
schemas/app/BatchNumber.json
Normal file
29
schemas/app/BatchNumber.json
Normal 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"]
|
||||
}
|
@ -97,8 +97,9 @@
|
||||
{
|
||||
"fieldname": "batchNumber",
|
||||
"label": "Batch No",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Link",
|
||||
"create": true,
|
||||
"target": "BatchNumber",
|
||||
"placeholder": "Batch No"
|
||||
}
|
||||
],
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
];
|
||||
|
@ -96,6 +96,11 @@ async function getInventorySidebar(): Promise<SidebarRoot[]> {
|
||||
name: 'stock-balance',
|
||||
route: '/report/StockBalance',
|
||||
},
|
||||
{
|
||||
label: t`Batch`,
|
||||
name: 'batch-number',
|
||||
route: '/list/BatchNumber',
|
||||
}
|
||||
],
|
||||
},
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user