2
0
mirror of https://github.com/frappe/books.git synced 2024-11-15 01:44:04 +00:00
books/models/inventory/tests/testInventory.spec.ts
2023-05-04 16:15:12 +05:30

375 lines
9.1 KiB
TypeScript

import {
assertDoesNotThrow,
assertThrows,
} from 'backend/database/tests/helpers';
import { ModelNameEnum } from 'models/types';
import { default as tape, default as test } from 'tape';
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
import { StockMovement } from '../StockMovement';
import { MovementTypeEnum } 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',
};
/**
* Section 1: Test Creation of Items and Locations
*/
test('create dummy items & locations', async (t) => {
// Create Items
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`);
}
// Create Locations
for (const name of Object.values(locationMap)) {
await fyo.doc.getNewDoc(ModelNameEnum.Location, { name }).sync();
t.ok(await fyo.db.exists(ModelNameEnum.Location, name), `${name} exists`);
}
});
/**
* Section 2: Test Creation of Stock Movements
*/
test('create stock movement, material receipt', async (t) => {
const { rate } = itemMap.Ink;
const quantity = 2;
const amount = rate * quantity;
const stockMovement = await getStockMovement(
MovementTypeEnum.MaterialReceipt,
new Date('2022-11-03T09:57:04.528'),
[
{
item: itemMap.Ink.name,
to: locationMap.LocationOne,
quantity,
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), 1);
t.equal(sle.item, itemMap.Ink.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.Ink.name), quantity);
});
test('create stock movement, material transfer', async (t) => {
const { rate } = itemMap.Ink;
const quantity = 2;
const stockMovement = await getStockMovement(
MovementTypeEnum.MaterialTransfer,
new Date('2022-11-03T09:58:04.528'),
[
{
item: itemMap.Ink.name,
from: locationMap.LocationOne,
to: locationMap.LocationTwo,
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.Ink.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.Ink.name, locationMap.LocationOne),
0
);
t.equal(await fyo.db.getStockQuantity(itemMap.Ink.name), quantity);
});
test('create stock movement, material issue', async (t) => {
const { rate } = itemMap.Ink;
const quantity = 2;
const stockMovement = await getStockMovement(
MovementTypeEnum.MaterialIssue,
new Date('2022-11-03T09:59:04.528'),
[
{
item: itemMap.Ink.name,
from: locationMap.LocationTwo,
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.Ink.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.Ink.name), 0);
});
/**
* Section 3: Test Cancellation of Stock Movements
*/
test('cancel stock movement', async (t) => {
const names = (await fyo.db.getAllRaw(ModelNameEnum.StockMovement)) as {
name: string;
}[];
for (const { name } of names) {
const slesBefore = await getSLEs(name, ModelNameEnum.StockMovement, fyo);
const doc = (await fyo.doc.getDoc(
ModelNameEnum.StockMovement,
name
)) as StockMovement;
if (doc.movementType === MovementTypeEnum.MaterialTransfer) {
t.equal(slesBefore.length, (doc.items?.length ?? 0) * 2);
} else {
t.equal(slesBefore.length, doc.items?.length ?? 0);
}
await doc.cancel();
const slesAfter = await getSLEs(name, ModelNameEnum.StockMovement, fyo);
t.equal(slesAfter.length, 0);
}
t.equal(await fyo.db.getStockQuantity(itemMap.Ink.name), null);
});
/**
* Section 4: Test Invalid entries
*/
async function runEntries(
item: string,
entries: {
type: MovementTypeEnum;
date: Date;
valid: boolean;
postQuantity: number;
items: {
item: string;
to?: string;
from?: string;
quantity: number;
rate: number;
}[];
}[],
t: tape.Test
) {
for (const { type, date, items, valid, postQuantity } of entries) {
const stockMovement = await getStockMovement(type, date, items, fyo);
await stockMovement.sync();
if (valid) {
await assertDoesNotThrow(async () => await stockMovement.submit());
} else {
await assertThrows(async () => await stockMovement.submit());
}
t.equal(await fyo.db.getStockQuantity(item), postQuantity);
}
}
test('create stock movements, invalid entries, in sequence', async (t) => {
const { name: item, rate } = itemMap.Pen;
const quantity = 10;
await runEntries(
item,
[
{
type: MovementTypeEnum.MaterialReceipt,
date: new Date('2022-11-03T09:58:04.528'),
valid: true,
postQuantity: quantity,
items: [
{
item,
to: locationMap.LocationOne,
quantity,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialTransfer,
date: new Date('2022-11-03T09:58:05.528'),
valid: false,
postQuantity: quantity,
items: [
{
item,
from: locationMap.LocationOne,
to: locationMap.LocationTwo,
quantity: quantity + 1,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialIssue,
date: new Date('2022-11-03T09:58:06.528'),
valid: false,
postQuantity: quantity,
items: [
{
item,
from: locationMap.LocationOne,
quantity: quantity + 1,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialTransfer,
date: new Date('2022-11-03T09:58:07.528'),
valid: true,
postQuantity: quantity,
items: [
{
item,
from: locationMap.LocationOne,
to: locationMap.LocationTwo,
quantity,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialIssue,
date: new Date('2022-11-03T09:58:08.528'),
valid: true,
postQuantity: 0,
items: [
{
item,
from: locationMap.LocationTwo,
quantity,
rate,
},
],
},
],
t
);
});
test('create stock movements, invalid entries, out of sequence', async (t) => {
const { name: item, rate } = itemMap.Ink;
const quantity = 10;
await runEntries(
item,
[
{
type: MovementTypeEnum.MaterialReceipt,
date: new Date('2022-11-15'),
valid: true,
postQuantity: quantity,
items: [
{
item,
to: locationMap.LocationOne,
quantity,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialIssue,
date: new Date('2022-11-17'),
valid: true,
postQuantity: quantity - 5,
items: [
{
item,
from: locationMap.LocationOne,
quantity: quantity - 5,
rate,
},
],
},
{
type: MovementTypeEnum.MaterialTransfer,
date: new Date('2022-11-16'),
valid: false,
postQuantity: quantity - 5,
items: [
{
item,
from: locationMap.LocationOne,
to: locationMap.LocationTwo,
quantity,
rate,
},
],
},
],
t
);
});
closeTestFyo(fyo, __filename);