mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
fix: stock movement
- test: stock movement create and cancel
This commit is contained in:
parent
2a5686058d
commit
43784984c3
@ -46,6 +46,7 @@ export const databaseMethodSet: Set<DatabaseMethod> = new Set([
|
|||||||
'rename',
|
'rename',
|
||||||
'update',
|
'update',
|
||||||
'delete',
|
'delete',
|
||||||
|
'deleteAll',
|
||||||
'close',
|
'close',
|
||||||
'exists',
|
'exists',
|
||||||
]);
|
]);
|
||||||
|
@ -129,7 +129,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
return !!this.submitted && !!this.cancelled;
|
return !!this.submitted && !!this.cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
get syncing() {
|
get isSyncing() {
|
||||||
return this._syncing;
|
return this._syncing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,13 +159,18 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
|
|
||||||
_setValuesWithoutChecks(data: DocValueMap, convertToDocValue: boolean) {
|
_setValuesWithoutChecks(data: DocValueMap, convertToDocValue: boolean) {
|
||||||
for (const field of this.schema.fields) {
|
for (const field of this.schema.fields) {
|
||||||
const fieldname = field.fieldname;
|
const { fieldname, fieldtype } = field;
|
||||||
const value = data[field.fieldname];
|
const value = data[field.fieldname];
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
for (const row of value) {
|
for (const row of value) {
|
||||||
this.push(fieldname, row, convertToDocValue);
|
this.push(fieldname, row, convertToDocValue);
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
fieldtype === FieldTypeEnum.Currency &&
|
||||||
|
typeof value === 'number'
|
||||||
|
) {
|
||||||
|
this[fieldname] = this.fyo.pesa(value);
|
||||||
} else if (value !== undefined && !convertToDocValue) {
|
} else if (value !== undefined && !convertToDocValue) {
|
||||||
this[fieldname] = value;
|
this[fieldname] = value;
|
||||||
} else if (value !== undefined) {
|
} else if (value !== undefined) {
|
||||||
@ -269,13 +274,13 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _applyChange(
|
async _applyChange(
|
||||||
fieldname: string,
|
changedFieldname: string,
|
||||||
retriggerChildDocApplyChange?: boolean
|
retriggerChildDocApplyChange?: boolean
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
await this._applyFormula(fieldname, retriggerChildDocApplyChange);
|
await this._applyFormula(changedFieldname, retriggerChildDocApplyChange);
|
||||||
await this.trigger('change', {
|
await this.trigger('change', {
|
||||||
doc: this,
|
doc: this,
|
||||||
changed: fieldname,
|
changed: changedFieldname,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -666,16 +671,17 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _applyFormula(
|
async _applyFormula(
|
||||||
fieldname?: string,
|
changedFieldname?: string,
|
||||||
retriggerChildDocApplyChange?: boolean
|
retriggerChildDocApplyChange?: boolean
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const doc = this;
|
const doc = this;
|
||||||
let changed = await this._callAllTableFieldsApplyFormula(fieldname);
|
let changed = await this._callAllTableFieldsApplyFormula(changedFieldname);
|
||||||
changed = (await this._applyFormulaForFields(doc, fieldname)) || changed;
|
changed =
|
||||||
|
(await this._applyFormulaForFields(doc, changedFieldname)) || changed;
|
||||||
|
|
||||||
if (changed && retriggerChildDocApplyChange) {
|
if (changed && retriggerChildDocApplyChange) {
|
||||||
await this._callAllTableFieldsApplyFormula(fieldname);
|
await this._callAllTableFieldsApplyFormula(changedFieldname);
|
||||||
await this._applyFormulaForFields(doc, fieldname);
|
await this._applyFormulaForFields(doc, changedFieldname);
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
|
@ -105,10 +105,45 @@ export function shouldApplyFormula(field: Field, doc: Doc, fieldname?: string) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (doc.isSyncing && dependsOn.length > 0) {
|
||||||
|
return shouldApplyFormulaPreSync(field.fieldname, dependsOn, doc);
|
||||||
|
}
|
||||||
|
|
||||||
const value = doc.get(field.fieldname);
|
const value = doc.get(field.fieldname);
|
||||||
return getIsNullOrUndef(value);
|
return getIsNullOrUndef(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldApplyFormulaPreSync(
|
||||||
|
fieldname: string,
|
||||||
|
dependsOn: string[],
|
||||||
|
doc: Doc
|
||||||
|
): boolean {
|
||||||
|
if (isDocValueTruthy(doc.get(fieldname))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const d of dependsOn) {
|
||||||
|
const isSet = isDocValueTruthy(doc.get(d));
|
||||||
|
if (isSet) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDocValueTruthy(docValue: DocValue | Doc[]) {
|
||||||
|
if (isPesa(docValue)) {
|
||||||
|
return !(docValue as Money).isZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(docValue)) {
|
||||||
|
return docValue.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!docValue;
|
||||||
|
}
|
||||||
|
|
||||||
export function setChildDocIdx(childDocs: Doc[]) {
|
export function setChildDocIdx(childDocs: Doc[]) {
|
||||||
for (const idx in childDocs) {
|
for (const idx in childDocs) {
|
||||||
childDocs[idx].idx = +idx;
|
childDocs[idx].idx = +idx;
|
||||||
|
@ -25,25 +25,41 @@ export class StockManager {
|
|||||||
this.fyo = fyo;
|
this.fyo = fyo;
|
||||||
}
|
}
|
||||||
|
|
||||||
async transferStock(transferDetails: SMTransferDetails) {
|
async createTransfers(transferDetails: SMTransferDetails[]) {
|
||||||
const details = this.#getSMIDetails(transferDetails);
|
for (const detail of transferDetails) {
|
||||||
const item = new StockManagerItem(details, this.fyo);
|
await this.#createTransfer(detail);
|
||||||
await item.transferStock(this.isCancelled);
|
}
|
||||||
this.items.push(item);
|
|
||||||
|
await this.#sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
async sync() {
|
async cancelTransfers() {
|
||||||
|
const { referenceName, referenceType } = this.details;
|
||||||
|
await this.fyo.db.deleteAll(ModelNameEnum.StockLedgerEntry, {
|
||||||
|
referenceType,
|
||||||
|
referenceName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async #sync() {
|
||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
await item.sync();
|
await item.sync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async #createTransfer(transferDetails: SMTransferDetails) {
|
||||||
|
const details = this.#getSMIDetails(transferDetails);
|
||||||
|
const item = new StockManagerItem(details, this.fyo);
|
||||||
|
await item.transferStock();
|
||||||
|
this.items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
#getSMIDetails(transferDetails: SMTransferDetails): SMIDetails {
|
#getSMIDetails(transferDetails: SMTransferDetails): SMIDetails {
|
||||||
return Object.assign({}, this.details, transferDetails);
|
return Object.assign({}, this.details, transferDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StockManagerItem {
|
class StockManagerItem {
|
||||||
/**
|
/**
|
||||||
* The Stock Manager Item is used to move stock to and from a location. It
|
* The Stock Manager Item is used to move stock to and from a location. It
|
||||||
* updates the Stock Queue and creates Stock Ledger Entries.
|
* updates the Stock Queue and creates Stock Ledger Entries.
|
||||||
@ -80,9 +96,9 @@ export class StockManagerItem {
|
|||||||
this.fyo = fyo;
|
this.fyo = fyo;
|
||||||
}
|
}
|
||||||
|
|
||||||
async transferStock(isCancelled: boolean) {
|
transferStock() {
|
||||||
this.#clear();
|
this.#clear();
|
||||||
await this.#moveStockForBothLocations(isCancelled);
|
this.#moveStockForBothLocations();
|
||||||
}
|
}
|
||||||
|
|
||||||
async sync() {
|
async sync() {
|
||||||
@ -91,29 +107,17 @@ export class StockManagerItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #moveStockForBothLocations(isCancelled: boolean) {
|
#moveStockForBothLocations() {
|
||||||
if (this.fromLocation) {
|
if (this.fromLocation) {
|
||||||
await this.#moveStockForSingleLocation(
|
this.#moveStockForSingleLocation(this.fromLocation, true);
|
||||||
this.fromLocation,
|
|
||||||
isCancelled ? false : true,
|
|
||||||
isCancelled
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.toLocation) {
|
if (this.toLocation) {
|
||||||
await this.#moveStockForSingleLocation(
|
this.#moveStockForSingleLocation(this.toLocation, false);
|
||||||
this.toLocation,
|
|
||||||
isCancelled ? true : false,
|
|
||||||
isCancelled
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #moveStockForSingleLocation(
|
#moveStockForSingleLocation(location: string, isOutward: boolean) {
|
||||||
location: string,
|
|
||||||
isOutward: boolean,
|
|
||||||
isCancelled: boolean
|
|
||||||
) {
|
|
||||||
let quantity = this.quantity!;
|
let quantity = this.quantity!;
|
||||||
if (quantity === 0) {
|
if (quantity === 0) {
|
||||||
return;
|
return;
|
||||||
@ -124,10 +128,8 @@ export class StockManagerItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stock Ledger Entry
|
// Stock Ledger Entry
|
||||||
if (!isCancelled) {
|
const stockLedgerEntry = this.#getStockLedgerEntry(location, quantity);
|
||||||
const stockLedgerEntry = this.#getStockLedgerEntry(location, quantity);
|
this.stockLedgerEntries?.push(stockLedgerEntry);
|
||||||
this.stockLedgerEntries?.push(stockLedgerEntry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#getStockLedgerEntry(location: string, quantity: number) {
|
#getStockLedgerEntry(location: string, quantity: number) {
|
||||||
|
@ -44,29 +44,22 @@ export class StockMovement extends Doc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async afterSubmit(): Promise<void> {
|
async afterSubmit(): Promise<void> {
|
||||||
await this._transferStock();
|
const transferDetails = this._getTransferDetails();
|
||||||
|
await this._getStockManager().createTransfers(transferDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterCancel(): Promise<void> {
|
async afterCancel(): Promise<void> {
|
||||||
await this._transferStock();
|
await this._getStockManager().cancelTransfers();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _transferStock() {
|
_getTransferDetails() {
|
||||||
const stockManager = this._getStockManager();
|
return (this.items ?? []).map((row) => ({
|
||||||
this._makeTransfers(stockManager);
|
item: row.item!,
|
||||||
await stockManager.sync();
|
rate: row.rate!,
|
||||||
}
|
quantity: row.quantity!,
|
||||||
|
fromLocation: row.fromLocation,
|
||||||
_makeTransfers(stockManager: StockManager) {
|
toLocation: row.toLocation,
|
||||||
for (const row of this.items ?? []) {
|
}));
|
||||||
stockManager.transferStock({
|
|
||||||
item: row.item!,
|
|
||||||
rate: row.rate!,
|
|
||||||
quantity: row.quantity!,
|
|
||||||
fromLocation: row.fromLocation,
|
|
||||||
toLocation: row.toLocation,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getStockManager(): StockManager {
|
_getStockManager(): StockManager {
|
||||||
|
@ -1,21 +1,64 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import test from 'tape';
|
import { StockMovement } from '../StockMovement';
|
||||||
|
import { MovementType } from '../types';
|
||||||
|
|
||||||
export const dummyItems = [
|
type SLE = {
|
||||||
{
|
date: string;
|
||||||
name: 'Ball Pen',
|
name: string;
|
||||||
rate: 50,
|
item: string;
|
||||||
for: 'Both',
|
location: string;
|
||||||
trackItem: true,
|
rate: string;
|
||||||
},
|
quantity: string;
|
||||||
{
|
};
|
||||||
name: 'Ink Pen',
|
|
||||||
rate: 700,
|
|
||||||
for: 'Both',
|
|
||||||
trackItem: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function createDummyItems(fyo: Fyo) {
|
type Transfer = {
|
||||||
|
item: string;
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
|
quantity: number;
|
||||||
|
rate: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getItem(name: string, rate: number) {
|
||||||
|
return { name, rate, trackItem: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStockMovement(
|
||||||
|
movementType: MovementType,
|
||||||
|
transfers: Transfer[],
|
||||||
|
fyo: Fyo
|
||||||
|
): Promise<StockMovement> {
|
||||||
|
const doc = fyo.doc.getNewDoc(ModelNameEnum.StockMovement, {
|
||||||
|
movementType,
|
||||||
|
}) as StockMovement;
|
||||||
|
|
||||||
|
for (const {
|
||||||
|
item,
|
||||||
|
from: fromLocation,
|
||||||
|
to: toLocation,
|
||||||
|
quantity,
|
||||||
|
rate,
|
||||||
|
} of transfers) {
|
||||||
|
await doc.append('items', {
|
||||||
|
item,
|
||||||
|
fromLocation,
|
||||||
|
toLocation,
|
||||||
|
rate,
|
||||||
|
quantity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSLEs(
|
||||||
|
referenceName: string,
|
||||||
|
referenceType: string,
|
||||||
|
fyo: Fyo
|
||||||
|
) {
|
||||||
|
return (await fyo.db.getAllRaw(ModelNameEnum.StockLedgerEntry, {
|
||||||
|
filters: { referenceName, referenceType },
|
||||||
|
fields: ['date', 'name', 'item', 'location', 'rate', 'quantity'],
|
||||||
|
})) as SLE[];
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,189 @@
|
|||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import test from 'tape';
|
import test from 'tape';
|
||||||
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
||||||
import { dummyItems } from './helpers';
|
import { StockMovement } from '../StockMovement';
|
||||||
|
import { MovementType } from '../types';
|
||||||
|
import { getItem, getSLEs, getStockMovement } from './helpers';
|
||||||
|
|
||||||
const fyo = getTestFyo();
|
const fyo = getTestFyo();
|
||||||
|
|
||||||
setupTestFyo(fyo, __filename);
|
setupTestFyo(fyo, __filename);
|
||||||
|
|
||||||
test('create dummy items', async (t) => {
|
const itemMap = {
|
||||||
for (const item of dummyItems) {
|
Pen: {
|
||||||
const doc = fyo.doc.getNewDoc(ModelNameEnum.Item, item);
|
name: 'Pen',
|
||||||
t.ok(await doc.sync(), `${item.name} created`);
|
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`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('check dummy items', async (t) => {
|
/**
|
||||||
for (const { name } of dummyItems) {
|
* Section 2: Test Creation of Stock Movements
|
||||||
const exists = await fyo.db.exists(ModelNameEnum.Item, name);
|
*/
|
||||||
t.ok(exists, `${name} exists`);
|
|
||||||
|
test('create stock movement, material receipt', async (t) => {
|
||||||
|
const { rate } = itemMap.Ink;
|
||||||
|
const quantity = 2;
|
||||||
|
const amount = rate * quantity;
|
||||||
|
const stockMovement = await getStockMovement(
|
||||||
|
MovementType.MaterialReceipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create stock movement, material transfer', async (t) => {
|
||||||
|
const { rate } = itemMap.Ink;
|
||||||
|
const quantity = 2;
|
||||||
|
|
||||||
|
const stockMovement = await getStockMovement(
|
||||||
|
MovementType.MaterialTransfer,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('create stock movement, material issue', async (t) => {
|
||||||
|
const { rate } = itemMap.Ink;
|
||||||
|
const quantity = 2;
|
||||||
|
|
||||||
|
const stockMovement = await getStockMovement(
|
||||||
|
MovementType.MaterialIssue,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 === MovementType.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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section 4: Test Invalid entries
|
||||||
|
*/
|
||||||
|
|
||||||
closeTestFyo(fyo, __filename);
|
closeTestFyo(fyo, __filename);
|
||||||
|
Loading…
Reference in New Issue
Block a user