From 869cdebed7a4c0d03ff85d794662679223ff9337 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 18:11:35 +0530 Subject: [PATCH 01/10] fix: hide UOM conversion and batches behind flags - add enable uom conversions flag - add enable batches flag - remove batch no from table fields and move to quick edit --- models/baseModels/InvoiceItem/InvoiceItem.ts | 10 +++++++--- models/baseModels/Item/Item.ts | 6 ++++-- models/inventory/StockMovementItem.ts | 11 +++++++++++ models/inventory/StockTransferItem.ts | 19 +++++++++++++++++-- schemas/app/InvoiceItem.json | 2 +- schemas/app/Item.json | 1 - schemas/app/inventory/InventorySettings.json | 10 ++++++++++ schemas/app/inventory/StockMovementItem.json | 2 +- schemas/app/inventory/StockTransferItem.json | 2 +- src/utils/search.ts | 5 +++++ 10 files changed, 57 insertions(+), 11 deletions(-) diff --git a/models/baseModels/InvoiceItem/InvoiceItem.ts b/models/baseModels/InvoiceItem/InvoiceItem.ts index 2e941038..ec93b471 100644 --- a/models/baseModels/InvoiceItem/InvoiceItem.ts +++ b/models/baseModels/InvoiceItem/InvoiceItem.ts @@ -430,9 +430,13 @@ export abstract class InvoiceItem extends Doc { !(this.enableDiscounting && !!this.setItemDiscountAmount), itemDiscountPercent: () => !(this.enableDiscounting && !this.setItemDiscountAmount), - transferUnit: () => !this.enableInventory, - transferQuantity: () => !this.enableInventory, - unitConversionFactor: () => !this.enableInventory, + batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + transferUnit: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + transferQuantity: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + unitConversionFactor: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, }; static filters: FiltersMap = { diff --git a/models/baseModels/Item/Item.ts b/models/baseModels/Item/Item.ts index c7a6b8e1..51193c6e 100644 --- a/models/baseModels/Item/Item.ts +++ b/models/baseModels/Item/Item.ts @@ -146,9 +146,11 @@ export class Item extends Doc { !this.fyo.singles.AccountingSettings?.enableInventory || this.itemType !== 'Product' || (this.inserted && !this.trackItem), - hasBatchNumber: () => !this.trackItem, barcode: () => !this.fyo.singles.InventorySettings?.enableBarcodes, - uomConversions: () => !this.fyo.singles.AccountingSettings?.enableInventory, + hasBatchNumber: () => + !(this.fyo.singles.InventorySettings?.enableBatches && this.trackItem), + uomConversions: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, }; readOnly: ReadOnlyMap = { diff --git a/models/inventory/StockMovementItem.ts b/models/inventory/StockMovementItem.ts index f1b385fb..12e16534 100644 --- a/models/inventory/StockMovementItem.ts +++ b/models/inventory/StockMovementItem.ts @@ -4,6 +4,7 @@ import { Doc } from 'fyo/model/doc'; import { FiltersMap, FormulaMap, + HiddenMap, ReadOnlyMap, RequiredMap, ValidationMap, @@ -233,6 +234,16 @@ export class StockMovementItem extends Doc { toLocation: () => this.isIssue, }; + override hidden: HiddenMap = { + batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + transferUnit: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + transferQuantity: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + unitConversionFactor: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + }; + static createFilters: FiltersMap = { item: () => ({ trackItem: true, itemType: 'Product' }), }; diff --git a/models/inventory/StockTransferItem.ts b/models/inventory/StockTransferItem.ts index 96f63547..2c5c2ccc 100644 --- a/models/inventory/StockTransferItem.ts +++ b/models/inventory/StockTransferItem.ts @@ -1,7 +1,12 @@ import { t } from 'fyo'; import { DocValue } from 'fyo/core/types'; import { Doc } from 'fyo/model/doc'; -import { FiltersMap, FormulaMap, ValidationMap } from 'fyo/model/types'; +import { + FiltersMap, + FormulaMap, + HiddenMap, + ValidationMap, +} from 'fyo/model/types'; import { ValidationError } from 'fyo/utils/errors'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; @@ -21,7 +26,7 @@ export class StockTransferItem extends Doc { amount?: Money; description?: string; hsnCode?: number; - batchNumber?: string + batchNumber?: string; formulas: FormulaMap = { description: { @@ -200,4 +205,14 @@ export class StockTransferItem extends Doc { return { for: ['not in', [itemNotFor]], trackItem: true }; }, }; + + override hidden: HiddenMap = { + batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + transferUnit: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + transferQuantity: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + unitConversionFactor: () => + !this.fyo.singles.InventorySettings?.enableUomConversions, + }; } diff --git a/schemas/app/InvoiceItem.json b/schemas/app/InvoiceItem.json index b6bbb522..b7d544c5 100644 --- a/schemas/app/InvoiceItem.json +++ b/schemas/app/InvoiceItem.json @@ -134,7 +134,7 @@ "readOnly": true } ], - "tableFields": ["item", "tax", "batchNumber", "quantity", "rate", "amount"], + "tableFields": ["item", "tax", "quantity", "rate", "amount"], "keywordFields": ["item", "tax"], "quickEditFields": [ "item", diff --git a/schemas/app/Item.json b/schemas/app/Item.json index 47d0901c..2420297b 100644 --- a/schemas/app/Item.json +++ b/schemas/app/Item.json @@ -158,7 +158,6 @@ "barcode", "hsnCode", "trackItem", - "hasBatchNumber", "uom" ], "keywordFields": ["name", "itemType", "for"] diff --git a/schemas/app/inventory/InventorySettings.json b/schemas/app/inventory/InventorySettings.json index b00252d9..0df7e6b4 100644 --- a/schemas/app/inventory/InventorySettings.json +++ b/schemas/app/inventory/InventorySettings.json @@ -50,6 +50,16 @@ "fieldname": "enableBarcodes", "label": "Enable Barcodes", "fieldtype": "Check" + }, + { + "fieldname": "enableBatches", + "label": "Enable Batches", + "fieldtype": "Check" + }, + { + "fieldname": "enableUomConversions", + "label": "Enable UOM Conversion", + "fieldtype": "Check" } ] } diff --git a/schemas/app/inventory/StockMovementItem.json b/schemas/app/inventory/StockMovementItem.json index cffb017c..e3f536cf 100644 --- a/schemas/app/inventory/StockMovementItem.json +++ b/schemas/app/inventory/StockMovementItem.json @@ -84,7 +84,7 @@ "readOnly": true } ], - "tableFields": ["item", "fromLocation", "toLocation", "batchNumber", "quantity", "rate"], + "tableFields": ["item", "fromLocation", "toLocation", "quantity", "rate"], "keywordFields": ["item"], "quickEditFields": [ "item", diff --git a/schemas/app/inventory/StockTransferItem.json b/schemas/app/inventory/StockTransferItem.json index 5c6eb207..3262a0b7 100644 --- a/schemas/app/inventory/StockTransferItem.json +++ b/schemas/app/inventory/StockTransferItem.json @@ -86,7 +86,7 @@ "placeholder": "HSN/SAC Code" } ], - "tableFields": ["item", "location", "batchNumber", "quantity", "rate", "amount"], + "tableFields": ["item", "location", "quantity", "rate", "amount"], "quickEditFields": [ "item", diff --git a/src/utils/search.ts b/src/utils/search.ts index 05832e73..cfa581d7 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -234,6 +234,11 @@ function getListViewList(fyo: Fyo): SearchItem[] { ); } + const hasBatch = fyo.doc.singles.InventorySettings?.enableBatches; + if (hasBatch) { + schemaNames.push(ModelNameEnum.BatchNumber); + } + if (fyo.store.isDevelopment) { schemaNames = Object.keys(fyo.schemaMap) as ModelNameEnum[]; } From 7ae697849e23a2239fb727a420a6d3709a7d9dd9 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 18:39:18 +0530 Subject: [PATCH 02/10] incr: validate if batch no set --- models/baseModels/Invoice/Invoice.ts | 1 + models/baseModels/Item/Item.ts | 2 +- models/inventory/StockMovement.ts | 6 +++++ models/inventory/StockTransfer.ts | 6 +++++ models/inventory/helpers.ts | 39 ++++++++++++++++++++++++++++ schemas/app/Item.json | 2 +- 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 01b21778..9e4d2620 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -11,6 +11,7 @@ import { import { DEFAULT_CURRENCY } from 'fyo/utils/consts'; import { ValidationError } from 'fyo/utils/errors'; import { addItem, getExchangeRate, getNumberSeries } from 'models/helpers'; +import { validateBatchNumber } from 'models/inventory/helpers'; import { InventorySettings } from 'models/inventory/InventorySettings'; import { StockTransfer } from 'models/inventory/StockTransfer'; import { Transactional } from 'models/Transactional/Transactional'; diff --git a/models/baseModels/Item/Item.ts b/models/baseModels/Item/Item.ts index 51193c6e..8bac0c06 100644 --- a/models/baseModels/Item/Item.ts +++ b/models/baseModels/Item/Item.ts @@ -93,7 +93,7 @@ export class Item extends Doc { if (itemEntriesInSLE > 0) { throw new ValidationError( - this.fyo.t`Cannot change value of Has Batch No as Item ${this + this.fyo.t`Cannot change value of Has Batch Number as Item ${this .name!} already has transactions against it. ` ); } diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index bc8d13c4..d539ec44 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -15,6 +15,7 @@ import { import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; +import { validateBatchNumber } from './helpers'; import { StockMovementItem } from './StockMovementItem'; import { Transfer } from './Transfer'; import { MovementType } from './types'; @@ -49,6 +50,11 @@ export class StockMovement extends Transfer { async validate() { await super.validate(); + this.validateManufacture(); + await validateBatchNumber(this); + } + + validateManufacture() { if (this.movementType !== MovementType.Manufacture) { return; } diff --git a/models/inventory/StockTransfer.ts b/models/inventory/StockTransfer.ts index fcbceaf7..18b28896 100644 --- a/models/inventory/StockTransfer.ts +++ b/models/inventory/StockTransfer.ts @@ -15,6 +15,7 @@ import { addItem, getLedgerLinkAction, getNumberSeries } from 'models/helpers'; import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; +import { validateBatchNumber } from './helpers'; import { StockTransferItem } from './StockTransferItem'; import { Transfer } from './Transfer'; @@ -151,6 +152,11 @@ export abstract class StockTransfer extends Transfer { } } + override async validate(): Promise { + await super.validate(); + await validateBatchNumber(this); + } + static getActions(fyo: Fyo): Action[] { return [getLedgerLinkAction(fyo, false), getLedgerLinkAction(fyo, true)]; } diff --git a/models/inventory/helpers.ts b/models/inventory/helpers.ts index 8b137891..8974f113 100644 --- a/models/inventory/helpers.ts +++ b/models/inventory/helpers.ts @@ -1 +1,40 @@ +import { ValidationError } from 'fyo/utils/errors'; +import { Invoice } from 'models/baseModels/Invoice/Invoice'; +import { InvoiceItem } from 'models/baseModels/InvoiceItem/InvoiceItem'; +import { ModelNameEnum } from 'models/types'; +import { StockMovement } from './StockMovement'; +import { StockMovementItem } from './StockMovementItem'; +import { StockTransfer } from './StockTransfer'; +import { StockTransferItem } from './StockTransferItem'; +export async function validateBatchNumber( + doc: StockMovement | StockTransfer | Invoice +) { + for (const row of doc.items ?? []) { + await validateItemRowBatchNumber(row); + } +} + +async function validateItemRowBatchNumber( + doc: StockMovementItem | StockTransferItem | InvoiceItem +) { + const item = doc.item; + const batchNumber = doc.batchNumber; + if (!item || batchNumber) { + return; + } + + const hasBatchNumber = await doc.fyo.getValue( + ModelNameEnum.Item, + item, + 'hasBatchNumber' + ); + + if (hasBatchNumber && batchNumber) { + return; + } + + throw new ValidationError( + [`Batch Number not set.`, `Item ${item} is a batched item`].join(' ') + ); +} diff --git a/schemas/app/Item.json b/schemas/app/Item.json index 2420297b..bd9bf838 100644 --- a/schemas/app/Item.json +++ b/schemas/app/Item.json @@ -133,7 +133,7 @@ }, { "fieldname": "hasBatchNumber", - "label": "Has Batch No", + "label": "Has Batch Number", "fieldtype": "Check", "default": false, "section": "Inventory" From 9646580b38e4ee21d78fb53b85d0d545c543c9cd Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 18:44:50 +0530 Subject: [PATCH 03/10] incr: make flags readonly once set - remove redundant validation --- models/baseModels/Item/Item.ts | 23 ----------------------- models/inventory/InventorySettings.ts | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/models/baseModels/Item/Item.ts b/models/baseModels/Item/Item.ts index 8bac0c06..f9cd4658 100644 --- a/models/baseModels/Item/Item.ts +++ b/models/baseModels/Item/Item.ts @@ -76,29 +76,6 @@ export class Item extends Doc { throw new ValidationError(this.fyo.t`Rate can't be negative.`); } }, - hasBatchNumber: async (value: DocValue) => { - const { hasBatchNumber } = await this.fyo.db.get( - 'Item', - this.name!, - 'hasBatchNumber' - ); - - if (hasBatchNumber && value !== hasBatchNumber) { - const itemEntriesInSLE = await this.fyo.db.count( - ModelNameEnum.StockLedgerEntry, - { - filters: { item: this.name! }, - } - ); - - if (itemEntriesInSLE > 0) { - throw new ValidationError( - this.fyo.t`Cannot change value of Has Batch Number as Item ${this - .name!} already has transactions against it. ` - ); - } - } - }, }; static getActions(fyo: Fyo): Action[] { diff --git a/models/inventory/InventorySettings.ts b/models/inventory/InventorySettings.ts index b6e3ff68..da79b82d 100644 --- a/models/inventory/InventorySettings.ts +++ b/models/inventory/InventorySettings.ts @@ -1,5 +1,5 @@ import { Doc } from 'fyo/model/doc'; -import { FiltersMap } from 'fyo/model/types'; +import { FiltersMap, ReadOnlyMap } from 'fyo/model/types'; import { AccountTypeEnum } from 'models/baseModels/Account/types'; import { ValuationMethod } from './types'; @@ -10,6 +10,8 @@ export class InventorySettings extends Doc { stockReceivedButNotBilled?: string; costOfGoodsSold?: string; enableBarcodes?: boolean; + enableBatches?: boolean; + enableUomConversions?: boolean; static filters: FiltersMap = { stockInHand: () => ({ @@ -25,4 +27,16 @@ export class InventorySettings extends Doc { accountType: AccountTypeEnum['Cost of Goods Sold'], }), }; + + readOnly: ReadOnlyMap = { + enableBarcodes: () => { + return !!this.enableBarcodes; + }, + enableBatches: () => { + return !!this.enableBatches; + }, + enableUomConversions: () => { + return !!this.enableUomConversions; + }, + }; } From 7029fa79ae71f9e8a95ed4637289faa64d373254 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 18:54:49 +0530 Subject: [PATCH 04/10] fix: valuation calculation when batches - use InvoiceItem batch on getStockTransfer --- models/baseModels/Invoice/Invoice.ts | 2 ++ models/baseModels/InvoiceItem/InvoiceItem.ts | 1 + reports/inventory/helpers.ts | 21 ++++++++++---------- reports/inventory/types.ts | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 9e4d2620..22e85dee 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -546,6 +546,7 @@ export abstract class Invoice extends Transactional { const item = row.item; const quantity = row.stockNotTransferred; const trackItem = itemDoc.trackItem; + const batchNumber = row.batchNumber || null; let rate = row.rate as Money; if (this.exchangeRate && this.exchangeRate > 1) { @@ -561,6 +562,7 @@ export abstract class Invoice extends Transactional { quantity, location, rate, + batchNumber, }); } diff --git a/models/baseModels/InvoiceItem/InvoiceItem.ts b/models/baseModels/InvoiceItem/InvoiceItem.ts index ec93b471..b70acb1a 100644 --- a/models/baseModels/InvoiceItem/InvoiceItem.ts +++ b/models/baseModels/InvoiceItem/InvoiceItem.ts @@ -29,6 +29,7 @@ export abstract class InvoiceItem extends Doc { quantity?: number; transferQuantity?: number; unitConversionFactor?: number; + batchNumber?: string; tax?: string; stockNotTransferred?: number; diff --git a/reports/inventory/helpers.ts b/reports/inventory/helpers.ts index c1bf70de..5881fc2c 100644 --- a/reports/inventory/helpers.ts +++ b/reports/inventory/helpers.ts @@ -11,6 +11,7 @@ import { type Item = string; type Location = string; +type BatchNo = string; export async function getRawStockLedgerEntries(fyo: Fyo) { const fieldnames = [ @@ -37,29 +38,27 @@ export function getStockLedgerEntries( valuationMethod: ValuationMethod ): ComputedStockLedgerEntry[] { const computedSLEs: ComputedStockLedgerEntry[] = []; - const stockQueues: Record> = {}; + const stockQueues: Record< + Item, + Record> + > = {}; for (const sle of rawSLEs) { 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, quantity, referenceName, referenceType } = sle; + const batchNumber = sle.batchNumber ?? ''; if (quantity === 0) { continue; } stockQueues[item] ??= {}; - stockQueues[item][location] ??= new StockQueue(); + stockQueues[item][location] ??= {}; + stockQueues[item][location][batchNumber] ??= new StockQueue(); - const q = stockQueues[item][location]; + const q = stockQueues[item][location][batchNumber]; const initialValue = q.value; let incomingRate: number | null; diff --git a/reports/inventory/types.ts b/reports/inventory/types.ts index ca433753..40531446 100644 --- a/reports/inventory/types.ts +++ b/reports/inventory/types.ts @@ -5,7 +5,7 @@ export interface RawStockLedgerEntry { date: string; item: string; rate: string; - batchNumber: string; + batchNumber: string | null; quantity: number; location: string; referenceName: string; From 9a8e423b72f2ad08aa2d28e02ccb2eeaa920ede3 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 19:16:04 +0530 Subject: [PATCH 05/10] fix: batch quantity validations --- models/inventory/StockManager.ts | 60 +++++++------------------------- models/inventory/helpers.ts | 23 ++++++++---- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/models/inventory/StockManager.ts b/models/inventory/StockManager.ts index f5d0adc5..8a914400 100644 --- a/models/inventory/StockManager.ts +++ b/models/inventory/StockManager.ts @@ -1,6 +1,5 @@ import { Fyo, t } from 'fyo'; import { ValidationError } from 'fyo/utils/errors'; -import { DateTime } from 'luxon'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; import { StockLedgerEntry } from './StockLedgerEntry'; @@ -133,43 +132,7 @@ export class StockManager { const date = details.date.toISOString(); const formattedDate = this.fyo.format(details.date, 'Datetime'); - const isItemHasBatchNumber = await this.fyo.getValue( - 'Item', - details.item, - 'hasBatchNumber' - ); - - if (isItemHasBatchNumber && !this.isCancelled) { - 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) return; - - throw new ValidationError( - [ - t`Insufficient Quantity`, - 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') - ); - } + const batchNumber = details.batchNumber || undefined; let quantityBefore = (await this.fyo.db.getStockQuantity( @@ -177,22 +140,24 @@ export class StockManager { details.fromLocation, undefined, date, - undefined + batchNumber )) ?? 0; if (this.isCancelled) { quantityBefore += details.quantity; } + const batchNumberMessage = !!batchNumber ? t` in Batch ${batchNumber}` : ''; + 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 - } on ${formattedDate}`, + }) required${batchNumberMessage} to make outward transfer of item ${ + details.item + } from ${details.fromLocation} on ${formattedDate}`, ].join('\n') ); } @@ -202,8 +167,9 @@ export class StockManager { details.fromLocation, details.date.toISOString(), undefined, - undefined + batchNumber ); + if (quantityAfter === null) { // No future transactions return; @@ -217,9 +183,9 @@ export class StockManager { 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 - } on ${formattedDate}`, + }) required${batchNumberMessage} to make outward transfer of item ${ + details.item + } from ${details.fromLocation} on ${formattedDate}`, ].join('\n') ); } @@ -312,7 +278,7 @@ class StockManagerItem { date: this.date, item: this.item, rate: this.rate, - batchNumber: this.batchNumber, + batchNumber: this.batchNumber || null, quantity, location, referenceName: this.referenceName, diff --git a/models/inventory/helpers.ts b/models/inventory/helpers.ts index 8974f113..e1b6bdba 100644 --- a/models/inventory/helpers.ts +++ b/models/inventory/helpers.ts @@ -18,9 +18,10 @@ export async function validateBatchNumber( async function validateItemRowBatchNumber( doc: StockMovementItem | StockTransferItem | InvoiceItem ) { + const idx = doc.idx ?? 0 + 1; const item = doc.item; const batchNumber = doc.batchNumber; - if (!item || batchNumber) { + if (!item) { return; } @@ -30,11 +31,21 @@ async function validateItemRowBatchNumber( 'hasBatchNumber' ); - if (hasBatchNumber && batchNumber) { - return; + if (!hasBatchNumber && batchNumber) { + throw new ValidationError( + [ + doc.fyo.t`Batch Number set for row ${idx}.`, + doc.fyo.t`Item ${item} is not a batched item`, + ].join(' ') + ); } - throw new ValidationError( - [`Batch Number not set.`, `Item ${item} is a batched item`].join(' ') - ); + if (hasBatchNumber && !batchNumber) { + throw new ValidationError( + [ + doc.fyo.t`Batch Number not set for row ${idx}.`, + doc.fyo.t`Item ${item} is a batched item`, + ].join(' ') + ); + } } From b3e4073bb795db1b077c44f585044608bb4bbaf8 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 27 Feb 2023 19:41:39 +0530 Subject: [PATCH 06/10] feat: batchwise filters in stock ledger and stock balance --- reports/inventory/StockBalance.ts | 22 ++++++++++++++++++++- reports/inventory/StockLedger.ts | 32 ++++++++++++++++++++++++++----- reports/inventory/helpers.ts | 22 ++++++++++++++------- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/reports/inventory/StockBalance.ts b/reports/inventory/StockBalance.ts index 4cd8944c..827bb057 100644 --- a/reports/inventory/StockBalance.ts +++ b/reports/inventory/StockBalance.ts @@ -25,9 +25,11 @@ export class StockBalance extends StockLedger { const filters = { item: this.item, location: this.location, + batch: this.batch, fromDate: this.fromDate, toDate: this.toDate, }; + const rawData = getStockBalanceEntries(this._rawData ?? [], filters); return rawData.map((sbe, i) => { @@ -41,7 +43,7 @@ export class StockBalance extends StockLedger { } getFilters(): Field[] { - return [ + const filters = [ { fieldtype: 'Link', target: 'Item', @@ -56,6 +58,17 @@ export class StockBalance extends StockLedger { label: t`Location`, fieldname: 'location', }, + ...(this.hasBatches + ? [ + { + fieldtype: 'Link', + target: 'BatchNumber', + placeholder: t`Batch`, + label: t`Batch`, + fieldname: 'batch', + }, + ] + : []), { fieldtype: 'Date', placeholder: t`From Date`, @@ -69,6 +82,8 @@ export class StockBalance extends StockLedger { fieldname: 'toDate', }, ] as Field[]; + + return filters; } getColumns(): ColumnField[] { @@ -89,6 +104,11 @@ export class StockBalance extends StockLedger { label: 'Location', fieldtype: 'Link', }, + ...(this.hasBatches + ? ([ + { fieldname: 'batch', label: 'Batch', fieldtype: 'Link' }, + ] as ColumnField[]) + : []), { fieldname: 'balanceQuantity', label: 'Balance Qty.', diff --git a/reports/inventory/StockLedger.ts b/reports/inventory/StockLedger.ts index 2150ed7a..bed69661 100644 --- a/reports/inventory/StockLedger.ts +++ b/reports/inventory/StockLedger.ts @@ -3,6 +3,7 @@ import { RawValueMap } from 'fyo/core/types'; import { Action } from 'fyo/model/types'; import { cloneDeep } from 'lodash'; import { DateTime } from 'luxon'; +import { InventorySettings } from 'models/inventory/InventorySettings'; import { ValuationMethod } from 'models/inventory/types'; import { ModelNameEnum } from 'models/types'; import getCommonExportActions from 'reports/commonExporter'; @@ -26,6 +27,7 @@ export class StockLedger extends Report { item?: string; location?: string; + batch?: string; fromDate?: string; toDate?: string; ascending?: boolean; @@ -34,6 +36,11 @@ export class StockLedger extends Report { groupBy: 'none' | 'item' | 'location' = 'none'; + get hasBatches(): boolean { + return !!(this.fyo.singles.InventorySettings as InventorySettings) + .enableBatches; + } + constructor(fyo: Fyo) { super(fyo); this._setObservers(); @@ -110,6 +117,10 @@ export class StockLedger extends Report { continue; } + if (this.batch && row.batchNumber !== this.batch) { + continue; + } + const date = row.date.valueOf(); if (toDate && date > toDate) { continue; @@ -260,11 +271,11 @@ export class StockLedger extends Report { label: 'Location', fieldtype: 'Link', }, - { - fieldname: 'batchNumber', - label: 'Batch No.', - fieldtype: 'Link', - }, + ...(this.hasBatches + ? ([ + { fieldname: 'batch', label: 'Batch', fieldtype: 'Link' }, + ] as ColumnField[]) + : []), { fieldname: 'quantity', label: 'Quantity', @@ -344,6 +355,17 @@ export class StockLedger extends Report { label: t`Location`, fieldname: 'location', }, + ...(this.hasBatches + ? ([ + { + fieldtype: 'Link', + target: 'BatchNumber', + placeholder: t`Batch`, + label: t`Batch`, + fieldname: 'batch', + }, + ] as Field[]) + : []), { fieldtype: 'Date', placeholder: t`From Date`, diff --git a/reports/inventory/helpers.ts b/reports/inventory/helpers.ts index 5881fc2c..e110a96f 100644 --- a/reports/inventory/helpers.ts +++ b/reports/inventory/helpers.ts @@ -117,7 +117,10 @@ export function getStockBalanceEntries( toDate?: string; } ): StockBalanceEntry[] { - const sbeMap: Record> = {}; + const sbeMap: Record< + Item, + Record> + > = {}; const fromDate = filters.fromDate ? Date.parse(filters.fromDate) : null; const toDate = filters.toDate ? Date.parse(filters.toDate) : null; @@ -131,16 +134,19 @@ export function getStockBalanceEntries( continue; } + const batchNumber = sle.batchNumber || ''; + sbeMap[sle.item] ??= {}; - sbeMap[sle.item][sle.location] ??= getSBE( + sbeMap[sle.item][sle.location] ??= {}; + sbeMap[sle.item][sle.location][batchNumber] ??= getSBE( sle.item, sle.location, - sle.batchNumber + batchNumber ); const date = sle.date.valueOf(); if (fromDate && date < fromDate) { - const sbe = sbeMap[sle.item][sle.location]!; + const sbe = sbeMap[sle.item][sle.location][batchNumber]; updateOpeningBalances(sbe, sle); continue; } @@ -149,13 +155,15 @@ export function getStockBalanceEntries( continue; } - const sbe = sbeMap[sle.item][sle.location]!; + const sbe = sbeMap[sle.item][sle.location][batchNumber]; updateCurrentBalances(sbe, sle); } return Object.values(sbeMap) - .map((sbes) => Object.values(sbes)) - .flat(); + .map((sbeBatched) => + Object.values(sbeBatched).map((sbes) => Object.values(sbes)) + ) + .flat(2); } function getSBE( From 71aabec935781b9459ffedaf3aed838e150f15f5 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 28 Feb 2023 11:25:48 +0530 Subject: [PATCH 07/10] refactor: shift batch tests to new file - remove redundant code from batch tests - update settings show reload toast trigger --- models/inventory/tests/helpers.ts | 8 +- models/inventory/tests/testBatches.spec.ts | 288 +++++++++++++++++++ models/inventory/tests/testInventory.spec.ts | 219 +------------- src/pages/Settings/Settings.vue | 28 +- 4 files changed, 315 insertions(+), 228 deletions(-) create mode 100644 models/inventory/tests/testBatches.spec.ts diff --git a/models/inventory/tests/helpers.ts b/models/inventory/tests/helpers.ts index 607dbe8b..df8dde60 100644 --- a/models/inventory/tests/helpers.ts +++ b/models/inventory/tests/helpers.ts @@ -36,7 +36,11 @@ interface TransferTwo extends Omit { location: string; } -export function getItem(name: string, rate: number, hasBatchNumber?: boolean) { +export function getItem( + name: string, + rate: number, + hasBatchNumber: boolean = false +) { return { name, rate, trackItem: true, hasBatchNumber }; } @@ -83,7 +87,7 @@ export async function getStockMovement( item, from: fromLocation, to: toLocation, - batchNumber: batchNumber, + batchNumber, quantity, rate, } of transfers) { diff --git a/models/inventory/tests/testBatches.spec.ts b/models/inventory/tests/testBatches.spec.ts new file mode 100644 index 00000000..4fed9ce7 --- /dev/null +++ b/models/inventory/tests/testBatches.spec.ts @@ -0,0 +1,288 @@ +import { assertThrows } from 'backend/database/tests/helpers'; +import { ModelNameEnum } from 'models/types'; +import test from 'tape'; +import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers'; +import { MovementType } from '../types'; +import { getItem, getSLEs, getStockMovement } from './helpers'; + +const fyo = getTestFyo(); + +setupTestFyo(fyo, __filename); + +const itemMap = { + Pen: { + name: 'Pen', + rate: 700, + }, + Ink: { + name: 'Ink', + rate: 50, + }, +}; + +const locationMap = { + LocationOne: 'LocationOne', + LocationTwo: 'LocationTwo', +}; + +const batchMap = { + batchOne: { + name: 'PN-AB001', + manufactureDate: '2022-11-03T09:57:04.528', + }, + batchTwo: { + name: 'PN-AB002', + manufactureDate: '2022-10-03T09:57:04.528', + }, + batchThree: { + name: 'PN-AB003', + manufactureDate: '2022-10-03T09:57:04.528', + }, +}; + +test('create dummy items, locations & batches', async (t) => { + // Create Items + for (const { name, rate } of Object.values(itemMap)) { + const item = getItem(name, rate, true); + await fyo.doc.getNewDoc(ModelNameEnum.Item, item).sync(); + } + + // Create Locations + for (const name of Object.values(locationMap)) { + await fyo.doc.getNewDoc(ModelNameEnum.Location, { name }).sync(); + } + + // Create Batches + for (const batch of Object.values(batchMap)) { + const doc = fyo.doc.getNewDoc(ModelNameEnum.BatchNumber, batch); + await doc.sync(); + + const exists = await fyo.db.exists(ModelNameEnum.BatchNumber, batch.name); + t.ok(exists, `${batch.name} exists`); + } +}); + +test('batched item, create stock movement, material receipt', async (t) => { + const { rate } = itemMap.Pen; + const stockMovement = await getStockMovement( + MovementType.MaterialReceipt, + new Date('2022-11-03T09:57:04.528'), + [ + { + item: itemMap.Pen.name, + to: locationMap.LocationOne, + quantity: 2, + batchNumber: batchMap.batchOne.name, + rate, + }, + { + item: itemMap.Pen.name, + to: locationMap.LocationOne, + quantity: 1, + batchNumber: batchMap.batchTwo.name, + rate, + }, + ], + fyo + ); + + await (await stockMovement.sync()).submit(); + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchMap.batchOne.name + ), + 2, + 'batch one has quantity two' + ); + + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchMap.batchTwo.name + ), + 1, + 'batch two has quantity one' + ); + + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchMap.batchThree.name + ), + null, + 'batch three has no quantity' + ); + + t.equal( + await fyo.db.getStockQuantity( + itemMap.Ink.name, + locationMap.LocationOne, + undefined, + undefined, + batchMap.batchOne.name + ), + null, + 'non transacted item has no quantity' + ); +}); + +test('batched item, create stock movement, material issue', async (t) => { + const { rate } = itemMap.Pen; + const quantity = 2; + const batchNumber = batchMap.batchOne.name; + + const stockMovement = await getStockMovement( + MovementType.MaterialIssue, + new Date('2022-11-03T10:00:00.528'), + [ + { + item: itemMap.Pen.name, + from: locationMap.LocationOne, + batchNumber, + quantity, + rate, + }, + ], + fyo + ); + + await (await stockMovement.sync()).submit(); + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchNumber + ), + 0, + 'batch one quantity transacted out' + ); + + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchMap.batchTwo.name + ), + 1, + 'batch two quantity intact' + ); +}); + +test('batched item, create stock movement, material transfer', async (t) => { + const { rate } = itemMap.Pen; + const quantity = 1; + const batchNumber = batchMap.batchTwo.name; + + const stockMovement = await getStockMovement( + MovementType.MaterialTransfer, + new Date('2022-11-03T09:58:04.528'), + [ + { + item: itemMap.Pen.name, + from: locationMap.LocationOne, + to: locationMap.LocationTwo, + batchNumber, + quantity, + rate, + }, + ], + fyo + ); + + await (await stockMovement.sync()).submit(); + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationOne, + undefined, + undefined, + batchNumber + ), + 0, + 'location one batch transacted out' + ); + + t.equal( + await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationTwo, + undefined, + undefined, + batchNumber + ), + quantity, + 'location two batch transacted in' + ); +}); + +test('batched item, create invalid stock movements', async (t) => { + const { name, rate } = itemMap.Pen; + const quantity = await fyo.db.getStockQuantity( + itemMap.Pen.name, + locationMap.LocationTwo, + undefined, + undefined, + batchMap.batchTwo.name + ); + + t.equal(quantity, 1, 'location two, batch one has quantity'); + if (!quantity) { + return; + } + + let stockMovement = await getStockMovement( + MovementType.MaterialIssue, + new Date('2022-11-03T09:59:04.528'), + [ + { + item: itemMap.Pen.name, + from: locationMap.LocationTwo, + batchNumber: batchMap.batchOne.name, + quantity, + rate, + }, + ], + fyo + ); + + await assertThrows( + async () => (await stockMovement.sync()).submit(), + 'invalid stockMovement with insufficient quantity did not throw' + ); + + stockMovement = await getStockMovement( + MovementType.MaterialIssue, + new Date('2022-11-03T09:59:04.528'), + [ + { + item: itemMap.Pen.name, + from: locationMap.LocationTwo, + quantity, + rate, + }, + ], + fyo + ); + + await assertThrows( + async () => (await stockMovement.sync()).submit(), + 'invalid stockMovement without batch did not throw' + ); + t.equal(await fyo.db.getStockQuantity(name), 1, 'item still has quantity'); +}); + +closeTestFyo(fyo, __filename); diff --git a/models/inventory/tests/testInventory.spec.ts b/models/inventory/tests/testInventory.spec.ts index 008c8369..69695832 100644 --- a/models/inventory/tests/testInventory.spec.ts +++ b/models/inventory/tests/testInventory.spec.ts @@ -17,12 +17,10 @@ const itemMap = { Pen: { name: 'Pen', rate: 700, - hasBatchNumber: true, }, Ink: { name: 'Ink', rate: 50, - hasBatchNumber: false, }, }; @@ -31,22 +29,14 @@ const locationMap = { LocationTwo: 'LocationTwo', }; -const batchNumberMap = { - batchNumberOne: { - name: 'IK-AB001', - manufactureDate: '2022-11-03T09:57:04.528', - expiryDate: '2023-11-03T09:57:04.528', - }, -}; - /** * Section 1: Test Creation of Items and Locations */ test('create dummy items & locations', async (t) => { // Create Items - for (const { name, rate, hasBatchNumber } of Object.values(itemMap)) { - const item = getItem(name, rate, hasBatchNumber); + for (const { name, rate } of Object.values(itemMap)) { + const item = getItem(name, rate); await fyo.doc.getNewDoc(ModelNameEnum.Item, item).sync(); t.ok(await fyo.db.exists(ModelNameEnum.Item, name), `${name} exists`); } @@ -58,25 +48,6 @@ test('create dummy items & locations', async (t) => { } }); -test('create dummy batch numbers', async (t) => { - // create batchNumber - for (const { name, manufactureDate, expiryDate } of Object.values( - batchNumberMap - )) { - await fyo.doc - .getNewDoc(ModelNameEnum.BatchNumber, { - name, - expiryDate, - manufactureDate, - }) - .sync(); - t.ok( - await fyo.db.exists(ModelNameEnum.BatchNumber, name), - `${name} exists` - ); - } -}); - /** * Section 2: Test Creation of Stock Movements */ @@ -119,55 +90,6 @@ test('create stock movement, material receipt', async (t) => { t.equal(await fyo.db.getStockQuantity(itemMap.Ink.name), quantity); }); -test('Batch Enabled : create stock movement, material receipt', async (t) => { - const { rate } = itemMap.Pen; - const quantity = 2; - const batchNumber = batchNumberMap.batchNumberOne.name; - const amount = rate * quantity; - const stockMovement = await getStockMovement( - MovementType.MaterialReceipt, - new Date('2022-11-03T09:57:04.528'), - [ - { - item: itemMap.Pen.name, - to: locationMap.LocationOne, - quantity, - batchNumber, - rate, - }, - ], - fyo - ); - - await (await stockMovement.sync()).submit(); - t.ok(stockMovement.name?.startsWith('SMOV-')); - t.equal(stockMovement.amount?.float, amount); - t.equal(stockMovement.items?.[0].amount?.float, amount); - - const name = stockMovement.name!; - - const sles = await getSLEs(name, ModelNameEnum.StockMovement, fyo); - t.equal(sles.length, 1); - - const sle = sles[0]; - t.notEqual(new Date(sle.date).toString(), 'Invalid Date'); - t.equal(parseInt(sle.name), 2); - t.equal(sle.item, itemMap.Pen.name); - t.equal(parseFloat(sle.rate), rate); - t.equal(sle.quantity, quantity); - t.equal(sle.location, locationMap.LocationOne); - t.equal( - await fyo.db.getStockQuantity( - itemMap.Pen.name, - locationMap.LocationOne, - undefined, - undefined, - batchNumber - ), - quantity - ); -}); - test('create stock movement, material transfer', async (t) => { const { rate } = itemMap.Ink; const quantity = 2; @@ -214,69 +136,6 @@ test('create stock movement, material transfer', async (t) => { t.equal(await fyo.db.getStockQuantity(itemMap.Ink.name), quantity); }); -test('Batch Enabled create stock movement, material transfer', async (t) => { - const { rate } = itemMap.Pen; - const quantity = 2; - const batchNumber = batchNumberMap.batchNumberOne.name; - - const stockMovement = await getStockMovement( - MovementType.MaterialTransfer, - new Date('2022-11-03T09:58:04.528'), - [ - { - item: itemMap.Pen.name, - from: locationMap.LocationOne, - to: locationMap.LocationTwo, - batchNumber, - quantity, - rate, - }, - ], - fyo - ); - - await (await stockMovement.sync()).submit(); - const name = stockMovement.name!; - - const sles = await getSLEs(name, ModelNameEnum.StockMovement, fyo); - t.equal(sles.length, 2); - - for (const sle of sles) { - t.notEqual(new Date(sle.date).toString(), 'Invalid Date'); - t.equal(sle.item, itemMap.Pen.name); - t.equal(parseFloat(sle.rate), rate); - - if (sle.location === locationMap.LocationOne) { - t.equal(sle.quantity, -quantity); - } else if (sle.location === locationMap.LocationTwo) { - t.equal(sle.quantity, quantity); - } else { - t.ok(false, 'no-op'); - } - } - - t.equal( - await fyo.db.getStockQuantity( - itemMap.Pen.name, - locationMap.LocationOne, - undefined, - undefined, - batchNumber - ), - 0 - ); - t.equal( - await fyo.db.getStockQuantity( - itemMap.Pen.name, - locationMap.LocationTwo, - undefined, - undefined, - batchNumber - ), - quantity - ); -}); - test('create stock movement, material issue', async (t) => { const { rate } = itemMap.Ink; const quantity = 2; @@ -310,50 +169,6 @@ test('create stock movement, material issue', async (t) => { t.equal(await fyo.db.getStockQuantity(itemMap.Ink.name), 0); }); -test('Batch Enabled create stock movement, material issue', async (t) => { - const { rate } = itemMap.Pen; - const quantity = 2; - const batchNumber = batchNumberMap.batchNumberOne.name; - - const stockMovement = await getStockMovement( - MovementType.MaterialIssue, - new Date('2022-11-03T09:59:04.528'), - [ - { - item: itemMap.Pen.name, - from: locationMap.LocationTwo, - batchNumber, - quantity, - rate, - }, - ], - fyo - ); - - await (await stockMovement.sync()).submit(); - const name = stockMovement.name!; - - const sles = await getSLEs(name, ModelNameEnum.StockMovement, fyo); - t.equal(sles.length, 1); - - const sle = sles[0]; - t.notEqual(new Date(sle.date).toString(), 'Invalid Date'); - t.equal(sle.item, itemMap.Pen.name); - t.equal(parseFloat(sle.rate), rate); - t.equal(sle.quantity, -quantity); - t.equal(sle.location, locationMap.LocationTwo); - t.equal( - await fyo.db.getStockQuantity( - itemMap.Pen.name, - undefined, - undefined, - undefined, - batchNumber - ), - 0 - ); -}); - /** * Section 3: Test Cancellation of Stock Movements */ @@ -399,7 +214,6 @@ async function runEntries( item: string; to?: string; from?: string; - batchNumber?: string; quantity: number; rate: number; }[]; @@ -435,7 +249,6 @@ test('create stock movements, invalid entries, in sequence', async (t) => { { item, to: locationMap.LocationOne, - batchNumber: batchNumberMap.batchNumberOne.name, quantity, rate, }, @@ -451,7 +264,6 @@ test('create stock movements, invalid entries, in sequence', async (t) => { item, from: locationMap.LocationOne, to: locationMap.LocationTwo, - batchNumber: batchNumberMap.batchNumberOne.name, quantity: quantity + 1, rate, }, @@ -466,7 +278,6 @@ test('create stock movements, invalid entries, in sequence', async (t) => { { item, from: locationMap.LocationOne, - batchNumber: batchNumberMap.batchNumberOne.name, quantity: quantity + 1, rate, }, @@ -481,7 +292,6 @@ test('create stock movements, invalid entries, in sequence', async (t) => { { item, from: locationMap.LocationOne, - batchNumber: batchNumberMap.batchNumberOne.name, to: locationMap.LocationTwo, quantity, rate, @@ -497,7 +307,6 @@ test('create stock movements, invalid entries, in sequence', async (t) => { { item, from: locationMap.LocationTwo, - batchNumber: batchNumberMap.batchNumberOne.name, quantity, rate, }, @@ -562,28 +371,4 @@ test('create stock movements, invalid entries, out of sequence', async (t) => { ); }); -test('create stock movements, material issue, insufficient quantity', async (t) => { - const { name, rate } = itemMap.Pen; - const quantity = 2; - const batchNumber = batchNumberMap.batchNumberOne.name; - - const stockMovement = await getStockMovement( - MovementType.MaterialIssue, - new Date('2022-11-03T09:59:04.528'), - [ - { - item: itemMap.Pen.name, - from: locationMap.LocationTwo, - batchNumber, - quantity, - rate, - }, - ], - fyo - ); - - await assertThrows(async () => (await stockMovement.sync()).submit()); - t.equal(await fyo.db.getStockQuantity(name), 0); -}); - closeTestFyo(fyo, __filename); diff --git a/src/pages/Settings/Settings.vue b/src/pages/Settings/Settings.vue index 2d0c2db7..0c1d4a4e 100644 --- a/src/pages/Settings/Settings.vue +++ b/src/pages/Settings/Settings.vue @@ -120,16 +120,26 @@ export default { if (this.fieldsChanged.length === 0) { return; } - const fieldnames = this.fieldsChanged.map(({ fieldname }) => fieldname); - if ( - fieldnames.includes('displayPrecision') || - fieldnames.includes('hideGetStarted') || - fieldnames.includes('displayPrecision') || - fieldnames.includes('enableDiscounting') || - fieldnames.includes('enableInventory') || - fieldnames.includes('enableBarcodes') - ) { + const shouleShowReload = this.fieldsChanged + .map(({ fieldname }) => fieldname) + .some((f) => { + if (f.startsWith('enable')) { + return true; + } + + if (f === 'displayPrecision') { + return true; + } + + if (f === 'hideGetStarted') { + return true; + } + + return false; + }); + + if (shouleShowReload) { this.showReloadToast(); } }, From 487f0498a84fc133391253ff82f96e3eb136d512 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 28 Feb 2023 11:31:04 +0530 Subject: [PATCH 08/10] refactor: rename Batch Number to Batch --- backend/database/bespoke.ts | 6 ++-- fyo/core/dbHandler.ts | 4 +-- models/baseModels/Invoice/Invoice.ts | 6 ++-- models/baseModels/InvoiceItem/InvoiceItem.ts | 4 +-- models/baseModels/Item/Item.ts | 6 ++-- models/index.ts | 4 +-- .../BatchNumber.ts => inventory/Batch.ts} | 2 +- models/inventory/StockLedgerEntry.ts | 2 +- models/inventory/StockManager.ts | 18 +++++------ models/inventory/StockMovement.ts | 6 ++-- models/inventory/StockMovementItem.ts | 4 +-- models/inventory/StockTransfer.ts | 6 ++-- models/inventory/StockTransferItem.ts | 4 +-- models/inventory/helpers.ts | 20 ++++++------- models/inventory/tests/helpers.ts | 28 ++++++++--------- models/inventory/tests/testBatches.spec.ts | 24 +++++++-------- models/inventory/types.ts | 2 +- models/types.ts | 2 +- reports/inventory/StockBalance.ts | 2 +- reports/inventory/StockLedger.ts | 4 +-- reports/inventory/helpers.ts | 30 +++++++++---------- reports/inventory/types.ts | 6 ++-- schemas/app/{BatchNumber.json => Batch.json} | 6 ++-- schemas/app/InvoiceItem.json | 10 +++---- schemas/app/Item.json | 4 +-- schemas/app/PrintSettings.json | 6 ++-- schemas/app/inventory/StockLedgerEntry.json | 6 ++-- schemas/app/inventory/StockMovementItem.json | 8 ++--- schemas/app/inventory/StockTransferItem.json | 8 ++--- schemas/schemas.ts | 4 +-- .../SalesInvoice/Templates/BaseTemplate.vue | 2 +- .../SalesInvoice/Templates/Basic.vue | 8 ++--- .../SalesInvoice/Templates/Business.vue | 8 ++--- .../SalesInvoice/Templates/Minimal.vue | 8 ++--- src/utils/search.ts | 2 +- 35 files changed, 133 insertions(+), 137 deletions(-) rename models/{baseModels/BatchNumber/BatchNumber.ts => inventory/Batch.ts} (85%) rename schemas/app/{BatchNumber.json => Batch.json} (85%) diff --git a/backend/database/bespoke.ts b/backend/database/bespoke.ts index 87c3ab7b..394c64bd 100644 --- a/backend/database/bespoke.ts +++ b/backend/database/bespoke.ts @@ -138,7 +138,7 @@ export class BespokeQueries { location?: string, fromDate?: string, toDate?: string, - batchNumber?: string + batch?: string ): Promise { const query = db.knex!(ModelNameEnum.StockLedgerEntry) .sum('quantity') @@ -148,8 +148,8 @@ export class BespokeQueries { query.andWhere('location', location); } - if (batchNumber) { - query.andWhere('batchNumber', batchNumber); + if (batch) { + query.andWhere('batch', batch); } if (fromDate) { diff --git a/fyo/core/dbHandler.ts b/fyo/core/dbHandler.ts index 0193aea4..070c6118 100644 --- a/fyo/core/dbHandler.ts +++ b/fyo/core/dbHandler.ts @@ -313,7 +313,7 @@ export class DatabaseHandler extends DatabaseBase { location?: string, fromDate?: string, toDate?: string, - batchNumber?: string + batch?: string ): Promise { return (await this.#demux.callBespoke( 'getStockQuantity', @@ -321,7 +321,7 @@ export class DatabaseHandler extends DatabaseBase { location, fromDate, toDate, - batchNumber + batch )) as number | null; } diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 22e85dee..b929c117 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -11,7 +11,7 @@ import { import { DEFAULT_CURRENCY } from 'fyo/utils/consts'; import { ValidationError } from 'fyo/utils/errors'; import { addItem, getExchangeRate, getNumberSeries } from 'models/helpers'; -import { validateBatchNumber } from 'models/inventory/helpers'; +import { validateBatch } from 'models/inventory/helpers'; import { InventorySettings } from 'models/inventory/InventorySettings'; import { StockTransfer } from 'models/inventory/StockTransfer'; import { Transactional } from 'models/Transactional/Transactional'; @@ -546,7 +546,7 @@ export abstract class Invoice extends Transactional { const item = row.item; const quantity = row.stockNotTransferred; const trackItem = itemDoc.trackItem; - const batchNumber = row.batchNumber || null; + const batch = row.batch || null; let rate = row.rate as Money; if (this.exchangeRate && this.exchangeRate > 1) { @@ -562,7 +562,7 @@ export abstract class Invoice extends Transactional { quantity, location, rate, - batchNumber, + batch, }); } diff --git a/models/baseModels/InvoiceItem/InvoiceItem.ts b/models/baseModels/InvoiceItem/InvoiceItem.ts index b70acb1a..670bbc13 100644 --- a/models/baseModels/InvoiceItem/InvoiceItem.ts +++ b/models/baseModels/InvoiceItem/InvoiceItem.ts @@ -29,7 +29,7 @@ export abstract class InvoiceItem extends Doc { quantity?: number; transferQuantity?: number; unitConversionFactor?: number; - batchNumber?: string; + batch?: string; tax?: string; stockNotTransferred?: number; @@ -431,7 +431,7 @@ export abstract class InvoiceItem extends Doc { !(this.enableDiscounting && !!this.setItemDiscountAmount), itemDiscountPercent: () => !(this.enableDiscounting && !this.setItemDiscountAmount), - batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + batch: () => !this.fyo.singles.InventorySettings?.enableBatches, transferUnit: () => !this.fyo.singles.InventorySettings?.enableUomConversions, transferQuantity: () => diff --git a/models/baseModels/Item/Item.ts b/models/baseModels/Item/Item.ts index f9cd4658..513b41f8 100644 --- a/models/baseModels/Item/Item.ts +++ b/models/baseModels/Item/Item.ts @@ -19,7 +19,7 @@ export class Item extends Doc { trackItem?: boolean; itemType?: 'Product' | 'Service'; for?: 'Purchases' | 'Sales' | 'Both'; - hasBatchNumber?: boolean; + hasBatch?: boolean; formulas: FormulaMap = { incomeAccount: { @@ -124,7 +124,7 @@ export class Item extends Doc { this.itemType !== 'Product' || (this.inserted && !this.trackItem), barcode: () => !this.fyo.singles.InventorySettings?.enableBarcodes, - hasBatchNumber: () => + hasBatch: () => !(this.fyo.singles.InventorySettings?.enableBatches && this.trackItem), uomConversions: () => !this.fyo.singles.InventorySettings?.enableUomConversions, @@ -134,6 +134,6 @@ export class Item extends Doc { unit: () => this.inserted, itemType: () => this.inserted, trackItem: () => this.inserted, - hasBatchNumber: () => this.inserted, + hasBatch: () => this.inserted, }; } diff --git a/models/index.ts b/models/index.ts index c12eb802..e8407771 100644 --- a/models/index.ts +++ b/models/index.ts @@ -26,14 +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'; +import { Batch } from './inventory/Batch'; export const models = { Account, AccountingLedgerEntry, AccountingSettings, Address, - BatchNumber, + Batch, Defaults, Item, JournalEntry, diff --git a/models/baseModels/BatchNumber/BatchNumber.ts b/models/inventory/Batch.ts similarity index 85% rename from models/baseModels/BatchNumber/BatchNumber.ts rename to models/inventory/Batch.ts index 0df86c78..27a04157 100644 --- a/models/baseModels/BatchNumber/BatchNumber.ts +++ b/models/inventory/Batch.ts @@ -3,7 +3,7 @@ import { ListViewSettings, } from 'fyo/model/types'; -export class BatchNumber extends Doc { +export class Batch extends Doc { static getListViewSettings(): ListViewSettings { return { columns: ["name", "expiryDate", "manufactureDate"], diff --git a/models/inventory/StockLedgerEntry.ts b/models/inventory/StockLedgerEntry.ts index 49e3ec38..d64c6756 100644 --- a/models/inventory/StockLedgerEntry.ts +++ b/models/inventory/StockLedgerEntry.ts @@ -10,5 +10,5 @@ export class StockLedgerEntry extends Doc { location?: string; referenceName?: string; referenceType?: string; - batchNumber?: string; + batch?: string; } diff --git a/models/inventory/StockManager.ts b/models/inventory/StockManager.ts index 8a914400..e000d4da 100644 --- a/models/inventory/StockManager.ts +++ b/models/inventory/StockManager.ts @@ -132,7 +132,7 @@ export class StockManager { const date = details.date.toISOString(); const formattedDate = this.fyo.format(details.date, 'Datetime'); - const batchNumber = details.batchNumber || undefined; + const batch = details.batch || undefined; let quantityBefore = (await this.fyo.db.getStockQuantity( @@ -140,14 +140,14 @@ export class StockManager { details.fromLocation, undefined, date, - batchNumber + batch )) ?? 0; if (this.isCancelled) { quantityBefore += details.quantity; } - const batchNumberMessage = !!batchNumber ? t` in Batch ${batchNumber}` : ''; + const batchMessage = !!batch ? t` in Batch ${batch}` : ''; if (quantityBefore < details.quantity) { throw new ValidationError( @@ -155,7 +155,7 @@ export class StockManager { t`Insufficient Quantity.`, t`Additional quantity (${ details.quantity - quantityBefore - }) required${batchNumberMessage} to make outward transfer of item ${ + }) required${batchMessage} to make outward transfer of item ${ details.item } from ${details.fromLocation} on ${formattedDate}`, ].join('\n') @@ -167,7 +167,7 @@ export class StockManager { details.fromLocation, details.date.toISOString(), undefined, - batchNumber + batch ); if (quantityAfter === null) { @@ -183,7 +183,7 @@ export class StockManager { t`Transfer will cause future entries to have negative stock.`, t`Additional quantity (${ quantityAfter - quantityRemaining - }) required${batchNumberMessage} to make outward transfer of item ${ + }) required${batchMessage} to make outward transfer of item ${ details.item } from ${details.fromLocation} on ${formattedDate}`, ].join('\n') @@ -210,7 +210,7 @@ class StockManagerItem { referenceType: string; fromLocation?: string; toLocation?: string; - batchNumber?: string; + batch?: string; stockLedgerEntries?: StockLedgerEntry[]; @@ -225,7 +225,7 @@ class StockManagerItem { this.toLocation = details.toLocation; this.referenceName = details.referenceName; this.referenceType = details.referenceType; - this.batchNumber = details.batchNumber; + this.batch = details.batch; this.fyo = fyo; } @@ -278,7 +278,7 @@ class StockManagerItem { date: this.date, item: this.item, rate: this.rate, - batchNumber: this.batchNumber || null, + batch: this.batch || null, quantity, location, referenceName: this.referenceName, diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index d539ec44..7d05429c 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -15,7 +15,7 @@ import { import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; -import { validateBatchNumber } from './helpers'; +import { validateBatch } from './helpers'; import { StockMovementItem } from './StockMovementItem'; import { Transfer } from './Transfer'; import { MovementType } from './types'; @@ -51,7 +51,7 @@ export class StockMovement extends Transfer { async validate() { await super.validate(); this.validateManufacture(); - await validateBatchNumber(this); + await validateBatch(this); } validateManufacture() { @@ -115,7 +115,7 @@ export class StockMovement extends Transfer { item: row.item!, rate: row.rate!, quantity: row.quantity!, - batchNumber: row.batchNumber!, + batch: row.batch!, fromLocation: row.fromLocation, toLocation: row.toLocation, })); diff --git a/models/inventory/StockMovementItem.ts b/models/inventory/StockMovementItem.ts index 12e16534..36318b21 100644 --- a/models/inventory/StockMovementItem.ts +++ b/models/inventory/StockMovementItem.ts @@ -31,7 +31,7 @@ export class StockMovementItem extends Doc { rate?: Money; amount?: Money; parentdoc?: StockMovement; - batchNumber?: string; + batch?: string; get isIssue() { return this.parentdoc?.movementType === MovementType.MaterialIssue; @@ -235,7 +235,7 @@ export class StockMovementItem extends Doc { }; override hidden: HiddenMap = { - batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + batch: () => !this.fyo.singles.InventorySettings?.enableBatches, transferUnit: () => !this.fyo.singles.InventorySettings?.enableUomConversions, transferQuantity: () => diff --git a/models/inventory/StockTransfer.ts b/models/inventory/StockTransfer.ts index 18b28896..1d8bb97f 100644 --- a/models/inventory/StockTransfer.ts +++ b/models/inventory/StockTransfer.ts @@ -15,7 +15,7 @@ import { addItem, getLedgerLinkAction, getNumberSeries } from 'models/helpers'; import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; -import { validateBatchNumber } from './helpers'; +import { validateBatch } from './helpers'; import { StockTransferItem } from './StockTransferItem'; import { Transfer } from './Transfer'; @@ -83,7 +83,7 @@ export abstract class StockTransfer extends Transfer { item: row.item!, rate: row.rate!, quantity: row.quantity!, - batchNumber: row.batchNumber!, + batch: row.batch!, fromLocation, toLocation, }; @@ -154,7 +154,7 @@ export abstract class StockTransfer extends Transfer { override async validate(): Promise { await super.validate(); - await validateBatchNumber(this); + await validateBatch(this); } static getActions(fyo: Fyo): Action[] { diff --git a/models/inventory/StockTransferItem.ts b/models/inventory/StockTransferItem.ts index 2c5c2ccc..ec42ca29 100644 --- a/models/inventory/StockTransferItem.ts +++ b/models/inventory/StockTransferItem.ts @@ -26,7 +26,7 @@ export class StockTransferItem extends Doc { amount?: Money; description?: string; hsnCode?: number; - batchNumber?: string; + batch?: string; formulas: FormulaMap = { description: { @@ -207,7 +207,7 @@ export class StockTransferItem extends Doc { }; override hidden: HiddenMap = { - batchNumber: () => !this.fyo.singles.InventorySettings?.enableBatches, + batch: () => !this.fyo.singles.InventorySettings?.enableBatches, transferUnit: () => !this.fyo.singles.InventorySettings?.enableUomConversions, transferQuantity: () => diff --git a/models/inventory/helpers.ts b/models/inventory/helpers.ts index e1b6bdba..ba1d22a6 100644 --- a/models/inventory/helpers.ts +++ b/models/inventory/helpers.ts @@ -7,43 +7,43 @@ import { StockMovementItem } from './StockMovementItem'; import { StockTransfer } from './StockTransfer'; import { StockTransferItem } from './StockTransferItem'; -export async function validateBatchNumber( +export async function validateBatch( doc: StockMovement | StockTransfer | Invoice ) { for (const row of doc.items ?? []) { - await validateItemRowBatchNumber(row); + await validateItemRowBatch(row); } } -async function validateItemRowBatchNumber( +async function validateItemRowBatch( doc: StockMovementItem | StockTransferItem | InvoiceItem ) { const idx = doc.idx ?? 0 + 1; const item = doc.item; - const batchNumber = doc.batchNumber; + const batch = doc.batch; if (!item) { return; } - const hasBatchNumber = await doc.fyo.getValue( + const hasBatch = await doc.fyo.getValue( ModelNameEnum.Item, item, - 'hasBatchNumber' + 'hasBatch' ); - if (!hasBatchNumber && batchNumber) { + if (!hasBatch && batch) { throw new ValidationError( [ - doc.fyo.t`Batch Number set for row ${idx}.`, + doc.fyo.t`Batch set for row ${idx}.`, doc.fyo.t`Item ${item} is not a batched item`, ].join(' ') ); } - if (hasBatchNumber && !batchNumber) { + if (hasBatch && !batch) { throw new ValidationError( [ - doc.fyo.t`Batch Number not set for row ${idx}.`, + doc.fyo.t`Batch not set for row ${idx}.`, doc.fyo.t`Item ${item} is a batched item`, ].join(' ') ); diff --git a/models/inventory/tests/helpers.ts b/models/inventory/tests/helpers.ts index df8dde60..245d859d 100644 --- a/models/inventory/tests/helpers.ts +++ b/models/inventory/tests/helpers.ts @@ -1,5 +1,5 @@ import { Fyo } from 'fyo'; -import { BatchNumber } from 'models/baseModels/BatchNumber/BatchNumber'; +import { Batch } from 'models/inventory/Batch'; import { ModelNameEnum } from 'models/types'; import { StockMovement } from '../StockMovement'; import { StockTransfer } from '../StockTransfer'; @@ -27,7 +27,7 @@ type Transfer = { item: string; from?: string; to?: string; - batchNumber?: string; + batch?: string; quantity: number; rate: number; }; @@ -36,26 +36,22 @@ interface TransferTwo extends Omit { location: string; } -export function getItem( - name: string, - rate: number, - hasBatchNumber: boolean = false -) { - return { name, rate, trackItem: true, hasBatchNumber }; +export function getItem(name: string, rate: number, hasBatch: boolean = false) { + return { name, rate, trackItem: true, hasBatch }; } -export async function getBatchNumber( - schemaName: ModelNameEnum.BatchNumber, - batchNumber: string, +export async function getBatch( + schemaName: ModelNameEnum.Batch, + batch: string, expiryDate: Date, manufactureDate: Date, fyo: Fyo -): Promise { +): Promise { const doc = fyo.doc.getNewDoc(schemaName, { - batchNumber, + batch, expiryDate, manufactureDate, - }) as BatchNumber; + }) as Batch; return doc; } @@ -87,7 +83,7 @@ export async function getStockMovement( item, from: fromLocation, to: toLocation, - batchNumber, + batch, quantity, rate, } of transfers) { @@ -95,7 +91,7 @@ export async function getStockMovement( item, fromLocation, toLocation, - batchNumber, + batch, rate, quantity, }); diff --git a/models/inventory/tests/testBatches.spec.ts b/models/inventory/tests/testBatches.spec.ts index 4fed9ce7..cba94ecf 100644 --- a/models/inventory/tests/testBatches.spec.ts +++ b/models/inventory/tests/testBatches.spec.ts @@ -54,10 +54,10 @@ test('create dummy items, locations & batches', async (t) => { // Create Batches for (const batch of Object.values(batchMap)) { - const doc = fyo.doc.getNewDoc(ModelNameEnum.BatchNumber, batch); + const doc = fyo.doc.getNewDoc(ModelNameEnum.Batch, batch); await doc.sync(); - const exists = await fyo.db.exists(ModelNameEnum.BatchNumber, batch.name); + const exists = await fyo.db.exists(ModelNameEnum.Batch, batch.name); t.ok(exists, `${batch.name} exists`); } }); @@ -72,14 +72,14 @@ test('batched item, create stock movement, material receipt', async (t) => { item: itemMap.Pen.name, to: locationMap.LocationOne, quantity: 2, - batchNumber: batchMap.batchOne.name, + batch: batchMap.batchOne.name, rate, }, { item: itemMap.Pen.name, to: locationMap.LocationOne, quantity: 1, - batchNumber: batchMap.batchTwo.name, + batch: batchMap.batchTwo.name, rate, }, ], @@ -139,7 +139,7 @@ test('batched item, create stock movement, material receipt', async (t) => { test('batched item, create stock movement, material issue', async (t) => { const { rate } = itemMap.Pen; const quantity = 2; - const batchNumber = batchMap.batchOne.name; + const batch = batchMap.batchOne.name; const stockMovement = await getStockMovement( MovementType.MaterialIssue, @@ -148,7 +148,7 @@ test('batched item, create stock movement, material issue', async (t) => { { item: itemMap.Pen.name, from: locationMap.LocationOne, - batchNumber, + batch, quantity, rate, }, @@ -163,7 +163,7 @@ test('batched item, create stock movement, material issue', async (t) => { locationMap.LocationOne, undefined, undefined, - batchNumber + batch ), 0, 'batch one quantity transacted out' @@ -185,7 +185,7 @@ test('batched item, create stock movement, material issue', async (t) => { test('batched item, create stock movement, material transfer', async (t) => { const { rate } = itemMap.Pen; const quantity = 1; - const batchNumber = batchMap.batchTwo.name; + const batch = batchMap.batchTwo.name; const stockMovement = await getStockMovement( MovementType.MaterialTransfer, @@ -195,7 +195,7 @@ test('batched item, create stock movement, material transfer', async (t) => { item: itemMap.Pen.name, from: locationMap.LocationOne, to: locationMap.LocationTwo, - batchNumber, + batch, quantity, rate, }, @@ -210,7 +210,7 @@ test('batched item, create stock movement, material transfer', async (t) => { locationMap.LocationOne, undefined, undefined, - batchNumber + batch ), 0, 'location one batch transacted out' @@ -222,7 +222,7 @@ test('batched item, create stock movement, material transfer', async (t) => { locationMap.LocationTwo, undefined, undefined, - batchNumber + batch ), quantity, 'location two batch transacted in' @@ -251,7 +251,7 @@ test('batched item, create invalid stock movements', async (t) => { { item: itemMap.Pen.name, from: locationMap.LocationTwo, - batchNumber: batchMap.batchOne.name, + batch: batchMap.batchOne.name, quantity, rate, }, diff --git a/models/inventory/types.ts b/models/inventory/types.ts index c07bc3e4..eab8769c 100644 --- a/models/inventory/types.ts +++ b/models/inventory/types.ts @@ -22,7 +22,7 @@ export interface SMTransferDetails { item: string; rate: Money; quantity: number; - batchNumber?: string; + batch?: string; fromLocation?: string; toLocation?: string; } diff --git a/models/types.ts b/models/types.ts index 61cbaf02..d6c4d3c2 100644 --- a/models/types.ts +++ b/models/types.ts @@ -4,7 +4,7 @@ export enum ModelNameEnum { AccountingLedgerEntry = 'AccountingLedgerEntry', AccountingSettings = 'AccountingSettings', Address = 'Address', - BatchNumber= 'BatchNumber', + Batch= 'Batch', Color = 'Color', CompanySettings = 'CompanySettings', Currency = 'Currency', diff --git a/reports/inventory/StockBalance.ts b/reports/inventory/StockBalance.ts index 827bb057..cf6795e8 100644 --- a/reports/inventory/StockBalance.ts +++ b/reports/inventory/StockBalance.ts @@ -62,7 +62,7 @@ export class StockBalance extends StockLedger { ? [ { fieldtype: 'Link', - target: 'BatchNumber', + target: 'Batch', placeholder: t`Batch`, label: t`Batch`, fieldname: 'batch', diff --git a/reports/inventory/StockLedger.ts b/reports/inventory/StockLedger.ts index bed69661..2544d8d9 100644 --- a/reports/inventory/StockLedger.ts +++ b/reports/inventory/StockLedger.ts @@ -117,7 +117,7 @@ export class StockLedger extends Report { continue; } - if (this.batch && row.batchNumber !== this.batch) { + if (this.batch && row.batch !== this.batch) { continue; } @@ -359,7 +359,7 @@ export class StockLedger extends Report { ? ([ { fieldtype: 'Link', - target: 'BatchNumber', + target: 'Batch', placeholder: t`Batch`, label: t`Batch`, fieldname: 'batch', diff --git a/reports/inventory/helpers.ts b/reports/inventory/helpers.ts index e110a96f..921fa4f2 100644 --- a/reports/inventory/helpers.ts +++ b/reports/inventory/helpers.ts @@ -11,14 +11,14 @@ import { type Item = string; type Location = string; -type BatchNo = string; +type Batch = string; export async function getRawStockLedgerEntries(fyo: Fyo) { const fieldnames = [ 'name', 'date', 'item', - 'batchNumber', + 'batch', 'rate', 'quantity', 'location', @@ -40,7 +40,7 @@ export function getStockLedgerEntries( const computedSLEs: ComputedStockLedgerEntry[] = []; const stockQueues: Record< Item, - Record> + Record> > = {}; for (const sle of rawSLEs) { @@ -48,7 +48,7 @@ export function getStockLedgerEntries( const date = new Date(sle.date); const rate = safeParseFloat(sle.rate); const { item, location, quantity, referenceName, referenceType } = sle; - const batchNumber = sle.batchNumber ?? ''; + const batch = sle.batch ?? ''; if (quantity === 0) { continue; @@ -56,9 +56,9 @@ export function getStockLedgerEntries( stockQueues[item] ??= {}; stockQueues[item][location] ??= {}; - stockQueues[item][location][batchNumber] ??= new StockQueue(); + stockQueues[item][location][batch] ??= new StockQueue(); - const q = stockQueues[item][location][batchNumber]; + const q = stockQueues[item][location][batch]; const initialValue = q.value; let incomingRate: number | null; @@ -87,7 +87,7 @@ export function getStockLedgerEntries( item, location, - batchNumber, + batch, quantity, balanceQuantity, @@ -119,7 +119,7 @@ export function getStockBalanceEntries( ): StockBalanceEntry[] { const sbeMap: Record< Item, - Record> + Record> > = {}; const fromDate = filters.fromDate ? Date.parse(filters.fromDate) : null; @@ -134,19 +134,19 @@ export function getStockBalanceEntries( continue; } - const batchNumber = sle.batchNumber || ''; + const batch = sle.batch || ''; sbeMap[sle.item] ??= {}; sbeMap[sle.item][sle.location] ??= {}; - sbeMap[sle.item][sle.location][batchNumber] ??= getSBE( + sbeMap[sle.item][sle.location][batch] ??= getSBE( sle.item, sle.location, - batchNumber + batch ); const date = sle.date.valueOf(); if (fromDate && date < fromDate) { - const sbe = sbeMap[sle.item][sle.location][batchNumber]; + const sbe = sbeMap[sle.item][sle.location][batch]; updateOpeningBalances(sbe, sle); continue; } @@ -155,7 +155,7 @@ export function getStockBalanceEntries( continue; } - const sbe = sbeMap[sle.item][sle.location][batchNumber]; + const sbe = sbeMap[sle.item][sle.location][batch]; updateCurrentBalances(sbe, sle); } @@ -169,14 +169,14 @@ export function getStockBalanceEntries( function getSBE( item: string, location: string, - batchNumber: string + batch: string ): StockBalanceEntry { return { name: 0, item, location, - batchNumber, + batch, balanceQuantity: 0, balanceValue: 0, diff --git a/reports/inventory/types.ts b/reports/inventory/types.ts index 40531446..af877e81 100644 --- a/reports/inventory/types.ts +++ b/reports/inventory/types.ts @@ -5,7 +5,7 @@ export interface RawStockLedgerEntry { date: string; item: string; rate: string; - batchNumber: string | null; + batch: string | null; quantity: number; location: string; referenceName: string; @@ -20,7 +20,7 @@ export interface ComputedStockLedgerEntry{ item: string; location:string; - batchNumber: string; + batch: string; quantity: number; balanceQuantity: number; @@ -41,7 +41,7 @@ export interface StockBalanceEntry{ item: string; location:string; - batchNumber: string; + batch: string; balanceQuantity: number; balanceValue: number; diff --git a/schemas/app/BatchNumber.json b/schemas/app/Batch.json similarity index 85% rename from schemas/app/BatchNumber.json rename to schemas/app/Batch.json index 6596d02f..251c215f 100644 --- a/schemas/app/BatchNumber.json +++ b/schemas/app/Batch.json @@ -1,12 +1,12 @@ { - "name": "BatchNumber", - "label": "Batch Number", + "name": "Batch", + "label": "Batch", "naming": "manual", "fields": [ { "fieldname": "name", "fieldtype": "Data", - "label": "Batch Number", + "label": "Batch", "required": true }, { diff --git a/schemas/app/InvoiceItem.json b/schemas/app/InvoiceItem.json index b7d544c5..c4abaa69 100644 --- a/schemas/app/InvoiceItem.json +++ b/schemas/app/InvoiceItem.json @@ -48,12 +48,12 @@ "readOnly": true }, { - "fieldname": "batchNumber", - "label": "Batch No", + "fieldname": "batch", + "label": "Batch", "fieldtype": "Link", "create": true, - "target": "BatchNumber", - "placeholder": "Batch No" + "target": "Batch", + "placeholder": "Batch" }, { "fieldname": "quantity", @@ -146,7 +146,7 @@ "transferQuantity", "transferUnit", - "batchNumber", + "batch", "quantity", "unit", "unitConversionFactor", diff --git a/schemas/app/Item.json b/schemas/app/Item.json index bd9bf838..83141278 100644 --- a/schemas/app/Item.json +++ b/schemas/app/Item.json @@ -132,8 +132,8 @@ "default": false }, { - "fieldname": "hasBatchNumber", - "label": "Has Batch Number", + "fieldname": "hasBatch", + "label": "Has Batch", "fieldtype": "Check", "default": false, "section": "Inventory" diff --git a/schemas/app/PrintSettings.json b/schemas/app/PrintSettings.json index 25fa62df..08f8f16c 100644 --- a/schemas/app/PrintSettings.json +++ b/schemas/app/PrintSettings.json @@ -30,8 +30,8 @@ "fieldtype": "Check" }, { - "fieldname": "displayBatchNumber", - "label": "Display Batch Number", + "fieldname": "displayBatch", + "label": "Display Batch", "fieldtype": "Check" }, { @@ -143,7 +143,7 @@ "logo", "displayLogo", "displayTaxInvoice", - "displayBatchNumber", + "displayBatch", "template", "color", "font", diff --git a/schemas/app/inventory/StockLedgerEntry.json b/schemas/app/inventory/StockLedgerEntry.json index 9f6dbda7..aefe17cb 100644 --- a/schemas/app/inventory/StockLedgerEntry.json +++ b/schemas/app/inventory/StockLedgerEntry.json @@ -51,10 +51,10 @@ "readOnly": true }, { - "fieldname": "batchNumber", - "label": "Batch No", + "fieldname": "batch", + "label": "Batch", "fieldtype": "Link", - "target": "BatchNumber", + "target": "Batch", "readOnly": true } ] diff --git a/schemas/app/inventory/StockMovementItem.json b/schemas/app/inventory/StockMovementItem.json index e3f536cf..a7bf4457 100644 --- a/schemas/app/inventory/StockMovementItem.json +++ b/schemas/app/inventory/StockMovementItem.json @@ -51,10 +51,10 @@ "readOnly": true }, { - "fieldname": "batchNumber", - "label": "Batch No", + "fieldname": "batch", + "label": "Batch", "fieldtype": "Link", - "target": "BatchNumber", + "target": "Batch", "create": true }, { @@ -93,7 +93,7 @@ "transferQuantity", "transferUnit", - "batchNumber", + "batch", "quantity", "unit", "unitConversionFactor", diff --git a/schemas/app/inventory/StockTransferItem.json b/schemas/app/inventory/StockTransferItem.json index 3262a0b7..03590954 100644 --- a/schemas/app/inventory/StockTransferItem.json +++ b/schemas/app/inventory/StockTransferItem.json @@ -42,10 +42,10 @@ "readOnly": true }, { - "fieldname": "batchNumber", - "label": "Batch No", + "fieldname": "batch", + "label": "Batch", "fieldtype": "Link", - "target": "BatchNumber" + "target": "Batch" }, { "fieldname": "quantity", @@ -92,7 +92,7 @@ "transferQuantity", "transferUnit", - "batchNumber", + "batch", "quantity", "unit", "unitConversionFactor", diff --git a/schemas/schemas.ts b/schemas/schemas.ts index 25ca0c26..8d4fdf0f 100644 --- a/schemas/schemas.ts +++ b/schemas/schemas.ts @@ -47,7 +47,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' +import Batch from './app/Batch.json' export const coreSchemas: Schema[] = [ PatchRun as Schema, @@ -116,5 +116,5 @@ export const appSchemas: Schema[] | SchemaStub[] = [ PurchaseReceipt as Schema, PurchaseReceiptItem as Schema, - BatchNumber as Schema + Batch as Schema ]; diff --git a/src/components/SalesInvoice/Templates/BaseTemplate.vue b/src/components/SalesInvoice/Templates/BaseTemplate.vue index 0d77e16f..e7b90e30 100644 --- a/src/components/SalesInvoice/Templates/BaseTemplate.vue +++ b/src/components/SalesInvoice/Templates/BaseTemplate.vue @@ -83,7 +83,7 @@ export default { showHSN: this.showHSN, displayLogo: this.printSettings.displayLogo, displayTaxInvoice: this.printSettings.displayTaxInvoice, - displayBatchNumber: this.printSettings.displayBatchNumber, + displayBatch: this.printSettings.displayBatch, discountAfterTax: this.doc.discountAfterTax, logo: this.printSettings.logo, companyName: this.fyo.singles.AccountingSettings.companyName, diff --git a/src/components/SalesInvoice/Templates/Basic.vue b/src/components/SalesInvoice/Templates/Basic.vue index d83520b3..04fc85fe 100644 --- a/src/components/SalesInvoice/Templates/Basic.vue +++ b/src/components/SalesInvoice/Templates/Basic.vue @@ -74,9 +74,9 @@
Quantity
- Batch No + Batch
Rate
Amount
@@ -93,9 +93,9 @@
{{ row.quantity }}
- {{ row.batchNumber }} + {{ row.batch }}
{{ row.rate }}
{{ row.amount }}
diff --git a/src/components/SalesInvoice/Templates/Business.vue b/src/components/SalesInvoice/Templates/Business.vue index 93162eee..b75c7e36 100644 --- a/src/components/SalesInvoice/Templates/Business.vue +++ b/src/components/SalesInvoice/Templates/Business.vue @@ -68,8 +68,8 @@
Item
HSN/SAC
Quantity
-
- Batch No +
+ Batch
Rate
Amount
@@ -84,8 +84,8 @@ {{ row.hsnCode }}
{{ row.quantity }}
-
- {{ row.batchNumber }} +
+ {{ row.batch }}
{{ row.rate }}
{{ row.amount }}
diff --git a/src/components/SalesInvoice/Templates/Minimal.vue b/src/components/SalesInvoice/Templates/Minimal.vue index 30e38cc3..9d2ec8e8 100644 --- a/src/components/SalesInvoice/Templates/Minimal.vue +++ b/src/components/SalesInvoice/Templates/Minimal.vue @@ -112,8 +112,8 @@
Item
HSN/SAC
Quantity
-
- Batch No +
+ Batch
Rate
Amount
@@ -128,8 +128,8 @@ {{ row.hsnCode }}
{{ row.quantity }}
-
- {{ row.batchNumber }} +
+ {{ row.batch }}
{{ row.rate }}
{{ row.amount }}
diff --git a/src/utils/search.ts b/src/utils/search.ts index cfa581d7..ddbcfb19 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -236,7 +236,7 @@ function getListViewList(fyo: Fyo): SearchItem[] { const hasBatch = fyo.doc.singles.InventorySettings?.enableBatches; if (hasBatch) { - schemaNames.push(ModelNameEnum.BatchNumber); + schemaNames.push(ModelNameEnum.Batch); } if (fyo.store.isDevelopment) { From 08cfaa6b687e4c1c9d86528de5377c2ac4d8fc37 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 28 Feb 2023 11:42:59 +0530 Subject: [PATCH 09/10] fix: batch filter in stock bal - allow viewing location if inventory is enabled --- reports/inventory/helpers.ts | 5 +++++ src/utils/search.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/reports/inventory/helpers.ts b/reports/inventory/helpers.ts index 921fa4f2..2b2ad7ed 100644 --- a/reports/inventory/helpers.ts +++ b/reports/inventory/helpers.ts @@ -115,6 +115,7 @@ export function getStockBalanceEntries( location?: string; fromDate?: string; toDate?: string; + batch?: string; } ): StockBalanceEntry[] { const sbeMap: Record< @@ -134,6 +135,10 @@ export function getStockBalanceEntries( continue; } + if (filters.batch && sle.batch !== filters.batch) { + continue; + } + const batch = sle.batch || ''; sbeMap[sle.item] ??= {}; diff --git a/src/utils/search.ts b/src/utils/search.ts index ddbcfb19..ea1b76a5 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -230,7 +230,8 @@ function getListViewList(fyo: Fyo): SearchItem[] { schemaNames.push( ModelNameEnum.StockMovement, ModelNameEnum.Shipment, - ModelNameEnum.PurchaseReceipt + ModelNameEnum.PurchaseReceipt, + ModelNameEnum.Location ); } From 68430a615e6918dbe19340e9dd457d801e9dc498 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 28 Feb 2023 12:09:23 +0530 Subject: [PATCH 10/10] chore: update doc link with batches --- src/utils/misc.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/misc.ts b/src/utils/misc.ts index d135172d..251e0b7d 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -108,6 +108,7 @@ export const docsPathMap: Record = { [ModelNameEnum.PurchaseReceipt]: 'inventory/purchase-receipt', StockLedger: 'inventory/stock-ledger', StockBalance: 'inventory/stock-balance', + [ModelNameEnum.Batch]: 'inventory/batches', // Entries Entries: 'entries/entries',