diff --git a/models/baseModels/InvoiceItem/InvoiceItem.ts b/models/baseModels/InvoiceItem/InvoiceItem.ts index 5288a39f..1622e1b4 100644 --- a/models/baseModels/InvoiceItem/InvoiceItem.ts +++ b/models/baseModels/InvoiceItem/InvoiceItem.ts @@ -209,10 +209,7 @@ export abstract class InvoiceItem extends Doc { } const validUnits = conversionItems.map((i) => i.uom as string); - if ( - this.transferUnit && - validUnits.includes(this.transferUnit as string) - ) { + if (this.transferUnit && validUnits.includes(this.transferUnit)) { return this.transferUnit; } @@ -612,19 +609,31 @@ export abstract class InvoiceItem extends Doc { return { for: ['not in', [itemNotFor]] }; }, - transferUnit: async (doc: Doc) => { - if (!doc.item) return {}; + batch: async (doc: Doc) => { + const batches = await doc.fyo.db.getAll(ModelNameEnum.Batch, { + fields: ['name'], + filters: { item: doc.item as string }, + }); + return { + name: ['in', batches.map((b) => b.name as string)], + }; + }, + transferUnit: async (doc: Doc) => { const conversionItems = await doc.fyo.db.getAll( ModelNameEnum.UOMConversionItem, { fields: ['uom'], - filters: { parent: doc.item }, + filters: { parent: doc.item as string }, } ); + const allUoms = [ + doc.unit, + ...conversionItems.map((i) => i.uom as string), + ]; return { - name: ['in', conversionItems.map((i) => i.uom as string)], + name: ['in', allUoms] as [string, string[]], }; }, }; diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index cd24b54b..b68da008 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -8,11 +8,7 @@ import { } from 'fyo/model/types'; import { ValidationError } from 'fyo/utils/errors'; import { LedgerPosting } from 'models/Transactional/LedgerPosting'; -import { - addItem, - getDocStatusListColumn, - getLedgerLinkAction, -} from 'models/helpers'; +import { getDocStatusListColumn, getLedgerLinkAction } from 'models/helpers'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; import { SerialNumber } from './SerialNumber'; @@ -141,7 +137,29 @@ export class StockMovement extends Transfer { } async addItem(name: string) { - return await addItem(name, this); + const itemDoc = await this.fyo.doc.getDoc(ModelNameEnum.Item, name); + if (!itemDoc) { + throw new ValidationError(t`Item ${name} not found`); + } + + const item = { + name: itemDoc.name, + batch: itemDoc.defaultBatch ?? null, + }; + + if (item.batch) { + const batchDoc = await this.fyo.doc.getDoc( + ModelNameEnum.Batch, + item.batch as string + ); + if (batchDoc && batchDoc.item !== name) { + throw new ValidationError( + t`Batch ${item.batch as string} does not belong to Item ${name}` + ); + } + } + + return item; } } diff --git a/models/inventory/StockMovementItem.ts b/models/inventory/StockMovementItem.ts index 613ce79f..17e641e0 100644 --- a/models/inventory/StockMovementItem.ts +++ b/models/inventory/StockMovementItem.ts @@ -56,6 +56,28 @@ export class StockMovementItem extends TransferItem { item: () => ({ trackItem: true }), }; + async validate() { + await super.validate(); + await this.validateBatchAndItemConsistency(); + } + + async validateBatchAndItemConsistency() { + if (!this.batch || !this.item) { + return; + } + + const batchDoc = await this.fyo.doc.getDoc(ModelNameEnum.Batch, this.batch); + if (!batchDoc) { + return; + } + + if (batchDoc.item !== this.item) { + throw new ValidationError( + t`Batch ${this.batch} does not belong to Item ${this.item}` + ); + } + } + formulas: FormulaMap = { rate: { formula: async () => { @@ -209,6 +231,13 @@ export class StockMovementItem extends TransferItem { ); } }, + batch: () => { + if (this.fyo.singles.InventorySettings?.enableBatches && !this.batch) { + throw new ValidationError( + t`Batch is required for Item ${this.item as string}` + ); + } + }, transferUnit: async (value: DocValue) => { if (!this.item) { return; diff --git a/schemas/app/Batch.json b/schemas/app/Batch.json index f0f0c3d6..cafe0812 100644 --- a/schemas/app/Batch.json +++ b/schemas/app/Batch.json @@ -29,6 +29,6 @@ "required": false } ], - "quickEditFields": ["expiryDate", "manufactureDate"], + "quickEditFields": ["item", "expiryDate", "manufactureDate"], "keywordFields": ["name"] }