mirror of
https://github.com/frappe/books.git
synced 2025-03-31 23:41:32 +00:00
fix: shift serial number creation to Transfer
This commit is contained in:
parent
4384e32706
commit
6869ca9652
@ -2,16 +2,10 @@ import { ListViewSettings } from 'fyo/model/types';
|
|||||||
import { getTransactionStatusColumn } from 'models/helpers';
|
import { getTransactionStatusColumn } from 'models/helpers';
|
||||||
import { PurchaseReceiptItem } from './PurchaseReceiptItem';
|
import { PurchaseReceiptItem } from './PurchaseReceiptItem';
|
||||||
import { StockTransfer } from './StockTransfer';
|
import { StockTransfer } from './StockTransfer';
|
||||||
import { createSerialNumbers } from './helpers';
|
|
||||||
|
|
||||||
export class PurchaseReceipt extends StockTransfer {
|
export class PurchaseReceipt extends StockTransfer {
|
||||||
items?: PurchaseReceiptItem[];
|
items?: PurchaseReceiptItem[];
|
||||||
|
|
||||||
override async afterSubmit(): Promise<void> {
|
|
||||||
await super.afterSubmit();
|
|
||||||
await createSerialNumbers(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getListViewSettings(): ListViewSettings {
|
static getListViewSettings(): ListViewSettings {
|
||||||
return {
|
return {
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Fyo, t } from 'fyo';
|
import { Fyo, t } from 'fyo';
|
||||||
import type { Doc } from 'fyo/model/doc';
|
|
||||||
import {
|
import {
|
||||||
Action,
|
Action,
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
@ -20,13 +19,13 @@ import { SerialNumber } from './SerialNumber';
|
|||||||
import { StockMovementItem } from './StockMovementItem';
|
import { StockMovementItem } from './StockMovementItem';
|
||||||
import { Transfer } from './Transfer';
|
import { Transfer } from './Transfer';
|
||||||
import {
|
import {
|
||||||
createSerialNumbers,
|
canValidateSerialNumber,
|
||||||
getSerialNumberFromDoc,
|
getSerialNumberFromDoc,
|
||||||
updateSerialNumbers,
|
updateSerialNumbers,
|
||||||
validateBatch,
|
validateBatch,
|
||||||
validateSerialNumber,
|
validateSerialNumber
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import { MovementType, MovementTypeEnum, SerialNumberStatus } from './types';
|
import { MovementType, MovementTypeEnum } from './types';
|
||||||
|
|
||||||
export class StockMovement extends Transfer {
|
export class StockMovement extends Transfer {
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -66,7 +65,6 @@ export class StockMovement extends Transfer {
|
|||||||
|
|
||||||
async afterSubmit(): Promise<void> {
|
async afterSubmit(): Promise<void> {
|
||||||
await super.afterSubmit();
|
await super.afterSubmit();
|
||||||
await createSerialNumbers(this);
|
|
||||||
await updateSerialNumbers(this, false);
|
await updateSerialNumbers(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +145,12 @@ export class StockMovement extends Transfer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function validateSerialNumberStatus(doc: StockMovement) {
|
async function validateSerialNumberStatus(doc: StockMovement) {
|
||||||
for (const serialNumber of getSerialNumberFromDoc(doc)) {
|
for (const { serialNumber, item } of getSerialNumberFromDoc(doc)) {
|
||||||
|
const cannotValidate = !(await canValidateSerialNumber(item, serialNumber));
|
||||||
|
if (cannotValidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const snDoc = await doc.fyo.doc.getDoc(
|
const snDoc = await doc.fyo.doc.getDoc(
|
||||||
ModelNameEnum.SerialNumber,
|
ModelNameEnum.SerialNumber,
|
||||||
serialNumber
|
serialNumber
|
||||||
@ -161,35 +164,26 @@ async function validateSerialNumberStatus(doc: StockMovement) {
|
|||||||
|
|
||||||
if (doc.movementType === 'MaterialReceipt' && status !== 'Inactive') {
|
if (doc.movementType === 'MaterialReceipt' && status !== 'Inactive') {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
t`Active Serial Number ${serialNumber} cannot be used for Material Issue`
|
t`Non Inactive Serial Number ${serialNumber} cannot be used for Material Receipt`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc.movementType === 'MaterialIssue' && status !== 'Active') {
|
if (doc.movementType === 'MaterialIssue' && status !== 'Active') {
|
||||||
validateMaterialIssueSerialNumber(serialNumber, status);
|
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
t`Inactive Serial Number ${serialNumber} cannot be used for Material Issue`
|
t`Non Active Serial Number ${serialNumber} cannot be used for Material Issue`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.movementType === 'MaterialTransfer' && status !== 'Active') {
|
||||||
|
throw new ValidationError(
|
||||||
|
t`Non Active Serial Number ${serialNumber} cannot be used for Material Transfer`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.fromLocation && status !== 'Active') {
|
||||||
|
throw new ValidationError(
|
||||||
|
t`Non Active Serial Number ${serialNumber} cannot be used as Manufacture raw material`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateMaterialReceiptSerialNumber(
|
|
||||||
serialNumber: string,
|
|
||||||
status: string
|
|
||||||
) {
|
|
||||||
if (status === 'Inactive') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function validateMaterialIssueSerialNumber(
|
|
||||||
serialNumber: string,
|
|
||||||
status: SerialNumberStatus
|
|
||||||
) {
|
|
||||||
if (status === 'Active') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ValidationError(t`Serial Number ${serialNumber} is not Active.`);
|
|
||||||
}
|
|
||||||
|
@ -15,8 +15,9 @@ import { Money } from 'pesa';
|
|||||||
import { safeParseFloat } from 'utils/index';
|
import { safeParseFloat } from 'utils/index';
|
||||||
import { StockMovement } from './StockMovement';
|
import { StockMovement } from './StockMovement';
|
||||||
import { MovementTypeEnum } from './types';
|
import { MovementTypeEnum } from './types';
|
||||||
|
import { TransferItem } from './TransferItem';
|
||||||
|
|
||||||
export class StockMovementItem extends Doc {
|
export class StockMovementItem extends TransferItem {
|
||||||
name?: string;
|
name?: string;
|
||||||
item?: string;
|
item?: string;
|
||||||
fromLocation?: string;
|
fromLocation?: string;
|
||||||
@ -30,10 +31,12 @@ export class StockMovementItem extends Doc {
|
|||||||
|
|
||||||
rate?: Money;
|
rate?: Money;
|
||||||
amount?: Money;
|
amount?: Money;
|
||||||
parentdoc?: StockMovement;
|
|
||||||
batch?: string;
|
batch?: string;
|
||||||
serialNumber?: string;
|
serialNumber?: string;
|
||||||
|
|
||||||
|
parentdoc?: StockMovement;
|
||||||
|
|
||||||
get isIssue() {
|
get isIssue() {
|
||||||
return this.parentdoc?.movementType === MovementTypeEnum.MaterialIssue;
|
return this.parentdoc?.movementType === MovementTypeEnum.MaterialIssue;
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,16 @@ import { addItem, getLedgerLinkAction, getNumberSeries } from 'models/helpers';
|
|||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { TargetField } from 'schemas/types';
|
import { TargetField } from 'schemas/types';
|
||||||
|
import { SerialNumber } from './SerialNumber';
|
||||||
import { StockTransferItem } from './StockTransferItem';
|
import { StockTransferItem } from './StockTransferItem';
|
||||||
import { Transfer } from './Transfer';
|
import { Transfer } from './Transfer';
|
||||||
import {
|
import {
|
||||||
|
canValidateSerialNumber,
|
||||||
getSerialNumberFromDoc,
|
getSerialNumberFromDoc,
|
||||||
updateSerialNumbers,
|
updateSerialNumbers,
|
||||||
validateBatch,
|
validateBatch,
|
||||||
validateSerialNumber,
|
validateSerialNumber,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import { SerialNumber } from './SerialNumber';
|
|
||||||
|
|
||||||
export abstract class StockTransfer extends Transfer {
|
export abstract class StockTransfer extends Transfer {
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -311,7 +312,12 @@ export abstract class StockTransfer extends Transfer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function validateSerialNumberStatus(doc: StockTransfer) {
|
async function validateSerialNumberStatus(doc: StockTransfer) {
|
||||||
for (const serialNumber of getSerialNumberFromDoc(doc)) {
|
for (const { serialNumber, item } of getSerialNumberFromDoc(doc)) {
|
||||||
|
const cannotValidate = !(await canValidateSerialNumber(item, serialNumber));
|
||||||
|
if (cannotValidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const snDoc = await doc.fyo.doc.getDoc(
|
const snDoc = await doc.fyo.doc.getDoc(
|
||||||
ModelNameEnum.SerialNumber,
|
ModelNameEnum.SerialNumber,
|
||||||
serialNumber
|
serialNumber
|
||||||
|
@ -12,8 +12,9 @@ import { ModelNameEnum } from 'models/types';
|
|||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { safeParseFloat } from 'utils/index';
|
import { safeParseFloat } from 'utils/index';
|
||||||
import { StockTransfer } from './StockTransfer';
|
import { StockTransfer } from './StockTransfer';
|
||||||
|
import { TransferItem } from './TransferItem';
|
||||||
|
|
||||||
export class StockTransferItem extends Doc {
|
export class StockTransferItem extends TransferItem {
|
||||||
item?: string;
|
item?: string;
|
||||||
location?: string;
|
location?: string;
|
||||||
|
|
||||||
@ -25,8 +26,10 @@ export class StockTransferItem extends Doc {
|
|||||||
|
|
||||||
rate?: Money;
|
rate?: Money;
|
||||||
amount?: Money;
|
amount?: Money;
|
||||||
|
|
||||||
description?: string;
|
description?: string;
|
||||||
hsnCode?: number;
|
hsnCode?: number;
|
||||||
|
|
||||||
batch?: string;
|
batch?: string;
|
||||||
serialNumber?: string;
|
serialNumber?: string;
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { Transactional } from 'models/Transactional/Transactional';
|
import { Transactional } from 'models/Transactional/Transactional';
|
||||||
import { StockManager } from './StockManager';
|
import { StockManager } from './StockManager';
|
||||||
import { SMTransferDetails } from './types';
|
import type { SMTransferDetails } from './types';
|
||||||
|
import type { TransferItem } from './TransferItem';
|
||||||
|
import { createSerialNumbers } from './helpers';
|
||||||
|
|
||||||
export abstract class Transfer extends Transactional {
|
export abstract class Transfer extends Transactional {
|
||||||
date?: Date;
|
date?: Date;
|
||||||
|
items?: TransferItem[];
|
||||||
|
|
||||||
async beforeSubmit(): Promise<void> {
|
async beforeSubmit(): Promise<void> {
|
||||||
await super.beforeSubmit();
|
await super.beforeSubmit();
|
||||||
@ -13,6 +16,7 @@ export abstract class Transfer extends Transactional {
|
|||||||
|
|
||||||
async afterSubmit(): Promise<void> {
|
async afterSubmit(): Promise<void> {
|
||||||
await super.afterSubmit();
|
await super.afterSubmit();
|
||||||
|
await createSerialNumbers(this);
|
||||||
const transferDetails = this._getTransferDetails();
|
const transferDetails = this._getTransferDetails();
|
||||||
await this._getStockManager().createTransfers(transferDetails);
|
await this._getStockManager().createTransfers(transferDetails);
|
||||||
}
|
}
|
||||||
|
21
models/inventory/TransferItem.ts
Normal file
21
models/inventory/TransferItem.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Doc } from 'fyo/model/doc';
|
||||||
|
import type { Transfer } from './Transfer';
|
||||||
|
import type { Money } from 'pesa';
|
||||||
|
|
||||||
|
export class TransferItem extends Doc {
|
||||||
|
item?: string;
|
||||||
|
|
||||||
|
unit?: string;
|
||||||
|
transferUnit?: string;
|
||||||
|
quantity?: number;
|
||||||
|
transferQuantity?: number;
|
||||||
|
unitConversionFactor?: number;
|
||||||
|
|
||||||
|
rate?: Money;
|
||||||
|
amount?: Money;
|
||||||
|
|
||||||
|
batch?: string;
|
||||||
|
serialNumber?: string;
|
||||||
|
|
||||||
|
parentdoc?: Transfer;
|
||||||
|
}
|
@ -8,8 +8,9 @@ import type { StockMovement } from './StockMovement';
|
|||||||
import type { StockMovementItem } from './StockMovementItem';
|
import type { StockMovementItem } from './StockMovementItem';
|
||||||
import type { StockTransfer } from './StockTransfer';
|
import type { StockTransfer } from './StockTransfer';
|
||||||
import type { StockTransferItem } from './StockTransferItem';
|
import type { StockTransferItem } from './StockTransferItem';
|
||||||
|
import { Transfer } from './Transfer';
|
||||||
|
import { TransferItem } from './TransferItem';
|
||||||
import type { SerialNumberStatus } from './types';
|
import type { SerialNumberStatus } from './types';
|
||||||
import type { PurchaseReceipt } from './PurchaseReceipt';
|
|
||||||
|
|
||||||
export async function validateBatch(
|
export async function validateBatch(
|
||||||
doc: StockMovement | StockTransfer | Invoice
|
doc: StockMovement | StockTransfer | Invoice
|
||||||
@ -171,14 +172,17 @@ export function getSerialNumberFromDoc(doc: StockTransfer | StockMovement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return doc.items
|
return doc.items
|
||||||
.map((item) => getSerialNumbers(item.serialNumber ?? ''))
|
.map((item) =>
|
||||||
|
getSerialNumbers(item.serialNumber ?? '').map((serialNumber) => ({
|
||||||
|
serialNumber,
|
||||||
|
item,
|
||||||
|
}))
|
||||||
|
)
|
||||||
.flat()
|
.flat()
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSerialNumbers(
|
export async function createSerialNumbers(doc: Transfer) {
|
||||||
doc: PurchaseReceipt | StockMovement
|
|
||||||
) {
|
|
||||||
const items = doc.items ?? [];
|
const items = doc.items ?? [];
|
||||||
const serialNumberCreateList = items
|
const serialNumberCreateList = items
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
@ -208,7 +212,7 @@ export async function createSerialNumbers(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSerialNumberIncoming(item: StockTransferItem | StockMovementItem) {
|
function isSerialNumberIncoming(item: TransferItem) {
|
||||||
if (item.parentdoc?.schemaName === ModelNameEnum.Shipment) {
|
if (item.parentdoc?.schemaName === ModelNameEnum.Shipment) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -220,6 +224,17 @@ function isSerialNumberIncoming(item: StockTransferItem | StockMovementItem) {
|
|||||||
return !!item.toLocation && !item.fromLocation;
|
return !!item.toLocation && !item.fromLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function canValidateSerialNumber(
|
||||||
|
item: StockTransferItem | StockMovementItem,
|
||||||
|
serialNumber: string
|
||||||
|
) {
|
||||||
|
if (!isSerialNumberIncoming(item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await item.fyo.db.exists(ModelNameEnum.SerialNumber, serialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateSerialNumbers(
|
export async function updateSerialNumbers(
|
||||||
doc: StockTransfer | StockMovement,
|
doc: StockTransfer | StockMovement,
|
||||||
isCancel: boolean
|
isCancel: boolean
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"default": "Inactive",
|
"default": "Inactive",
|
||||||
|
"readOnly": true,
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"value": "Inactive",
|
"value": "Inactive",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user