2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

fix: serial number validation

- add serial number creation for strictly incoming
This commit is contained in:
18alantom 2023-05-05 10:37:00 +05:30
parent 0d80548c36
commit 4384e32706
3 changed files with 80 additions and 20 deletions

View File

@ -2,10 +2,16 @@ 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: [

View File

@ -20,6 +20,7 @@ import { SerialNumber } from './SerialNumber';
import { StockMovementItem } from './StockMovementItem'; import { StockMovementItem } from './StockMovementItem';
import { Transfer } from './Transfer'; import { Transfer } from './Transfer';
import { import {
createSerialNumbers,
getSerialNumberFromDoc, getSerialNumberFromDoc,
updateSerialNumbers, updateSerialNumbers,
validateBatch, validateBatch,
@ -65,6 +66,7 @@ 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);
} }
@ -163,7 +165,7 @@ async function validateSerialNumberStatus(doc: StockMovement) {
); );
} }
if (doc.movementType === 'MaterialIssue' && status !=='Active') { if (doc.movementType === 'MaterialIssue' && status !== 'Active') {
validateMaterialIssueSerialNumber(serialNumber, status); validateMaterialIssueSerialNumber(serialNumber, status);
throw new ValidationError( throw new ValidationError(
t`Inactive Serial Number ${serialNumber} cannot be used for Material Issue` t`Inactive Serial Number ${serialNumber} cannot be used for Material Issue`

View File

@ -9,6 +9,7 @@ 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 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
@ -65,10 +66,6 @@ async function validateItemRowSerialNumber(
return; return;
} }
if (row.parentdoc?.cancelled) {
return;
}
const hasSerialNumber = await row.fyo.getValue( const hasSerialNumber = await row.fyo.getValue(
ModelNameEnum.Item, ModelNameEnum.Item,
item, item,
@ -99,13 +96,6 @@ async function validateItemRowSerialNumber(
} }
const serialNumbers = getSerialNumbers(serialNumber); const serialNumbers = getSerialNumbers(serialNumber);
for (const serialNumber of serialNumbers) {
if (await row.fyo.db.exists(ModelNameEnum.SerialNumber, serialNumber)) {
continue;
}
throw new ValidationError(t`Serial Number ${serialNumber} does not exist.`);
}
const quantity = row.quantity ?? 0; const quantity = row.quantity ?? 0;
if (serialNumbers.length !== quantity) { if (serialNumbers.length !== quantity) {
@ -116,7 +106,25 @@ async function validateItemRowSerialNumber(
); );
} }
const nonExistingIncomingSerialNumbers: string[] = [];
for (const serialNumber of serialNumbers) { for (const serialNumber of serialNumbers) {
if (await row.fyo.db.exists(ModelNameEnum.SerialNumber, serialNumber)) {
continue;
}
if (isSerialNumberIncoming(row)) {
nonExistingIncomingSerialNumbers.push(serialNumber);
continue;
}
throw new ValidationError(t`Serial Number ${serialNumber} does not exist.`);
}
for (const serialNumber of serialNumbers) {
if (nonExistingIncomingSerialNumbers.includes(serialNumber)) {
continue;
}
const snDoc = await row.fyo.doc.getDoc( const snDoc = await row.fyo.doc.getDoc(
ModelNameEnum.SerialNumber, ModelNameEnum.SerialNumber,
serialNumber serialNumber
@ -168,6 +176,50 @@ export function getSerialNumberFromDoc(doc: StockTransfer | StockMovement) {
.filter(Boolean); .filter(Boolean);
} }
export async function createSerialNumbers(
doc: PurchaseReceipt | StockMovement
) {
const items = doc.items ?? [];
const serialNumberCreateList = items
.map((item) => {
const serialNumbers = getSerialNumbers(item.serialNumber ?? '');
return serialNumbers.map((serialNumber) => ({
item: item.name ?? '',
serialNumber,
isIncoming: isSerialNumberIncoming(item),
}));
})
.flat()
.filter(({ item, isIncoming }) => isIncoming && item);
for (const { item, serialNumber } of serialNumberCreateList) {
if (await doc.fyo.db.exists(ModelNameEnum.SerialNumber, serialNumber)) {
continue;
}
const snDoc = doc.fyo.doc.getNewDoc(ModelNameEnum.SerialNumber, {
name: serialNumber,
item,
});
const status: SerialNumberStatus = 'Active';
await snDoc.set('status', status);
await snDoc.sync();
}
}
function isSerialNumberIncoming(item: StockTransferItem | StockMovementItem) {
if (item.parentdoc?.schemaName === ModelNameEnum.Shipment) {
return false;
}
if (item.parentdoc?.schemaName === ModelNameEnum.PurchaseReceipt) {
return true;
}
return !!item.toLocation && !item.fromLocation;
}
export async function updateSerialNumbers( export async function updateSerialNumbers(
doc: StockTransfer | StockMovement, doc: StockTransfer | StockMovement,
isCancel: boolean isCancel: boolean
@ -177,7 +229,7 @@ export async function updateSerialNumbers(
continue; continue;
} }
const status = getSerialNumberStatus(doc, isCancel, row.quantity ?? 0); const status = getSerialNumberStatus(doc, row, isCancel);
await updateSerialNumberStatus(status, row.serialNumber, doc.fyo); await updateSerialNumberStatus(status, row.serialNumber, doc.fyo);
} }
} }
@ -197,8 +249,8 @@ async function updateSerialNumberStatus(
function getSerialNumberStatus( function getSerialNumberStatus(
doc: StockTransfer | StockMovement, doc: StockTransfer | StockMovement,
isCancel: boolean, item: StockTransferItem | StockMovementItem,
quantity: number isCancel: boolean
): SerialNumberStatus { ): SerialNumberStatus {
if (doc.schemaName === ModelNameEnum.Shipment) { if (doc.schemaName === ModelNameEnum.Shipment) {
return isCancel ? 'Active' : 'Delivered'; return isCancel ? 'Active' : 'Delivered';
@ -210,15 +262,15 @@ function getSerialNumberStatus(
return getSerialNumberStatusForStockMovement( return getSerialNumberStatusForStockMovement(
doc as StockMovement, doc as StockMovement,
isCancel, item,
quantity isCancel
); );
} }
function getSerialNumberStatusForStockMovement( function getSerialNumberStatusForStockMovement(
doc: StockMovement, doc: StockMovement,
isCancel: boolean, item: StockTransferItem | StockMovementItem,
quantity: number isCancel: boolean
): SerialNumberStatus { ): SerialNumberStatus {
if (doc.movementType === 'MaterialIssue') { if (doc.movementType === 'MaterialIssue') {
return isCancel ? 'Active' : 'Inactive'; return isCancel ? 'Active' : 'Inactive';
@ -233,7 +285,7 @@ function getSerialNumberStatusForStockMovement(
} }
// MovementType is Manufacture // MovementType is Manufacture
if (quantity < 0) { if (item.fromLocation) {
return isCancel ? 'Active' : 'Inactive'; return isCancel ? 'Active' : 'Inactive';
} }