diff --git a/.eslintrc.js b/.eslintrc.js index 7574bf64..2b09c43c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,7 @@ module.exports = { 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'arrow-body-style': 'off', 'prefer-arrow-callback': 'off', + 'vue/no-mutating-props': 'off', 'vue/multi-word-component-names': 'off', 'vue/no-useless-template-attributes': 'off', }, diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index 25fabee1..2a157e83 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -6,7 +6,12 @@ import { FormulaMap, ListViewSettings, } from 'fyo/model/types'; -import { addItem, getDocStatusListColumn, getLedgerLinkAction } from 'models/helpers'; +import { ValidationError } from 'fyo/utils/errors'; +import { + addItem, + getDocStatusListColumn, + getLedgerLinkAction, +} from 'models/helpers'; import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; @@ -42,6 +47,24 @@ export class StockMovement extends Transfer { }, }; + async validate() { + await super.validate(); + if (this.movementType !== MovementType.Manufacture) { + return; + } + + const hasFrom = this.items?.findIndex((f) => f.fromLocation) !== -1; + const hasTo = this.items?.findIndex((f) => f.toLocation) !== -1; + + if (!hasFrom) { + throw new ValidationError(this.fyo.t`Item with From location not found`); + } + + if (!hasTo) { + throw new ValidationError(this.fyo.t`Item with To location not found`); + } + } + static filters: FiltersMap = { numberSeries: () => ({ referenceType: ModelNameEnum.StockMovement }), }; @@ -68,6 +91,7 @@ export class StockMovement extends Transfer { [MovementType.MaterialIssue]: fyo.t`Material Issue`, [MovementType.MaterialReceipt]: fyo.t`Material Receipt`, [MovementType.MaterialTransfer]: fyo.t`Material Transfer`, + [MovementType.Manufacture]: fyo.t`Manufacture`, }[movementType] ?? ''; return { diff --git a/models/inventory/StockMovementItem.ts b/models/inventory/StockMovementItem.ts index 665852a1..da012873 100644 --- a/models/inventory/StockMovementItem.ts +++ b/models/inventory/StockMovementItem.ts @@ -4,7 +4,9 @@ import { FormulaMap, ReadOnlyMap, RequiredMap, + ValidationMap, } from 'fyo/model/types'; +import { ValidationError } from 'fyo/utils/errors'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; import { StockMovement } from './StockMovement'; @@ -32,6 +34,10 @@ export class StockMovementItem extends Doc { return this.parentdoc?.movementType === MovementType.MaterialTransfer; } + get isManufacture() { + return this.parentdoc?.movementType === MovementType.Manufacture; + } + static filters: FiltersMap = { item: () => ({ trackItem: true }), }; @@ -52,14 +58,14 @@ export class StockMovementItem extends Doc { dependsOn: ['item', 'rate', 'quantity'], }, fromLocation: { - formula: (fn) => { - if (this.isReceipt || this.isTransfer) { + formula: () => { + if (this.isReceipt || this.isTransfer || this.isManufacture) { return null; } const defaultLocation = this.fyo.singles.InventorySettings ?.defaultLocation as string | undefined; - if (defaultLocation && !this.location && this.isIssue) { + if (defaultLocation && !this.fromLocation && this.isIssue) { return defaultLocation; } @@ -68,14 +74,14 @@ export class StockMovementItem extends Doc { dependsOn: ['movementType'], }, toLocation: { - formula: (fn) => { - if (this.isIssue || this.isTransfer) { + formula: () => { + if (this.isIssue || this.isTransfer || this.isManufacture) { return null; } const defaultLocation = this.fyo.singles.InventorySettings ?.defaultLocation as string | undefined; - if (defaultLocation && !this.location && this.isReceipt) { + if (defaultLocation && !this.toLocation && this.isReceipt) { return defaultLocation; } @@ -85,6 +91,31 @@ export class StockMovementItem extends Doc { }, }; + validations: ValidationMap = { + fromLocation: (value) => { + if (!this.isManufacture) { + return; + } + + if (value && this.toLocation) { + throw new ValidationError( + this.fyo.t`Only From or To can be set for Manucature` + ); + } + }, + toLocation: (value) => { + if (!this.isManufacture) { + return; + } + + if (value && this.fromLocation) { + throw new ValidationError( + this.fyo.t`Only From or To can be set for Manufacture` + ); + } + }, + }; + required: RequiredMap = { fromLocation: () => this.isIssue || this.isTransfer, toLocation: () => this.isReceipt || this.isTransfer, diff --git a/models/inventory/types.ts b/models/inventory/types.ts index 6a846b1a..80ecb81d 100644 --- a/models/inventory/types.ts +++ b/models/inventory/types.ts @@ -9,6 +9,7 @@ export enum MovementType { 'MaterialIssue' = 'MaterialIssue', 'MaterialReceipt' = 'MaterialReceipt', 'MaterialTransfer' = 'MaterialTransfer', + 'Manufacture' = 'Manufacture', } export interface SMDetails { diff --git a/schemas/app/inventory/StockMovement.json b/schemas/app/inventory/StockMovement.json index b4d41684..99651250 100644 --- a/schemas/app/inventory/StockMovement.json +++ b/schemas/app/inventory/StockMovement.json @@ -35,6 +35,10 @@ { "value": "MaterialTransfer", "label": "Material Transfer" + }, + { + "value": "Manufacture", + "label": "Manufacture" } ], "required": true diff --git a/src/components/Controls/TableRow.vue b/src/components/Controls/TableRow.vue index 6824adfb..bef30b88 100644 --- a/src/components/Controls/TableRow.vue +++ b/src/components/Controls/TableRow.vue @@ -62,6 +62,7 @@ import { Doc } from 'fyo/model/doc'; import Row from 'src/components/Row.vue'; import { getErrorMessage } from 'src/utils'; +import { nextTick } from 'vue'; import Button from '../Button.vue'; import FormControl from './FormControl.vue'; @@ -102,15 +103,18 @@ export default { }, }, methods: { - onChange(df, value) { - if (value == null) { - return; - } + async onChange(df, value) { + const fieldname = df.fieldname; + this.errors[fieldname] = null; + const oldValue = this.row[fieldname]; - this.errors[df.fieldname] = null; - this.row.set(df.fieldname, value).catch((e) => { - this.errors[df.fieldname] = getErrorMessage(e, this.row); - }); + try { + await this.row.set(fieldname, value); + } catch (e) { + this.errors[fieldname] = getErrorMessage(e, this.row); + this.row[fieldname] = ''; + nextTick(() => (this.row[fieldname] = oldValue)); + } }, getErrorString() { return Object.values(this.errors).filter(Boolean).join(' ');