mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
feat: add shipment and purchase receipt
This commit is contained in:
parent
14138967c1
commit
9a510f1a63
@ -857,6 +857,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.trigger('beforeCancel');
|
||||||
await this.trigger('beforeCancel');
|
await this.trigger('beforeCancel');
|
||||||
await this.setAndSync('cancelled', true);
|
await this.setAndSync('cancelled', true);
|
||||||
await this.trigger('afterCancel');
|
await this.trigger('afterCancel');
|
||||||
@ -908,6 +909,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
if (convertToFloat) {
|
if (convertToFloat) {
|
||||||
return sum.float;
|
return sum.float;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,28 +26,56 @@ export abstract class Transactional extends Doc {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getPosting(): Promise<LedgerPosting>;
|
abstract getPosting(): Promise<LedgerPosting | null>;
|
||||||
|
|
||||||
async validate() {
|
async validate() {
|
||||||
await super.validate();
|
await super.validate();
|
||||||
|
if (!this.isTransactional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const posting = await this.getPosting();
|
const posting = await this.getPosting();
|
||||||
|
if (posting === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
posting.validate();
|
posting.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterSubmit(): Promise<void> {
|
async afterSubmit(): Promise<void> {
|
||||||
await super.afterSubmit();
|
await super.afterSubmit();
|
||||||
|
if (!this.isTransactional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const posting = await this.getPosting();
|
const posting = await this.getPosting();
|
||||||
|
if (posting === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await posting.post();
|
await posting.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterCancel(): Promise<void> {
|
async afterCancel(): Promise<void> {
|
||||||
await super.afterCancel();
|
await super.afterCancel();
|
||||||
|
if (!this.isTransactional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const posting = await this.getPosting();
|
const posting = await this.getPosting();
|
||||||
|
if (posting === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await posting.postReverse();
|
await posting.postReverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterDelete(): Promise<void> {
|
async afterDelete(): Promise<void> {
|
||||||
await super.afterDelete();
|
await super.afterDelete();
|
||||||
|
if (!this.isTransactional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const ledgerEntryIds = (await this.fyo.db.getAll(
|
const ledgerEntryIds = (await this.fyo.db.getAll(
|
||||||
ModelNameEnum.AccountingLedgerEntry,
|
ModelNameEnum.AccountingLedgerEntry,
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { FiltersMap } from 'fyo/model/types';
|
import { FiltersMap } from 'fyo/model/types';
|
||||||
import { AccountTypeEnum } from 'models/baseModels/Account/types';
|
import { AccountTypeEnum } from 'models/baseModels/Account/types';
|
||||||
import { valuationMethod } from './types';
|
import { ValuationMethod } from './types';
|
||||||
|
|
||||||
export class InventorySettings extends Doc {
|
export class InventorySettings extends Doc {
|
||||||
stockInHand?: string;
|
stockInHand?: string;
|
||||||
valuationMethod?: valuationMethod;
|
valuationMethod?: ValuationMethod;
|
||||||
stockInReceivedButNotBilled?: string;
|
stockReceivedButNotBilled?: string;
|
||||||
|
costOfGoodsSold?: string;
|
||||||
|
|
||||||
static filters: FiltersMap = {
|
static filters: FiltersMap = {
|
||||||
stockInHand: () => ({
|
stockInHand: () => ({
|
||||||
@ -17,5 +18,9 @@ export class InventorySettings extends Doc {
|
|||||||
isGroup: false,
|
isGroup: false,
|
||||||
accountType: AccountTypeEnum['Stock Received But Not Billed'],
|
accountType: AccountTypeEnum['Stock Received But Not Billed'],
|
||||||
}),
|
}),
|
||||||
|
costOfGoodsSold: () => ({
|
||||||
|
isGroup: false,
|
||||||
|
accountType: AccountTypeEnum['Cost of Goods Sold'],
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Fyo, t } from 'fyo';
|
import { Fyo, t } from 'fyo';
|
||||||
import { ValidationError } from 'fyo/utils/errors';
|
import { ValidationError } from 'fyo/utils/errors';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { StockLedgerEntry } from './StockLedgerEntry';
|
import { StockLedgerEntry } from './StockLedgerEntry';
|
||||||
@ -53,6 +54,19 @@ export class StockManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async validateCancel(transferDetails: SMTransferDetails[]) {
|
||||||
|
const reverseTransferDetails = transferDetails.map(
|
||||||
|
({ item, rate, quantity, fromLocation, toLocation }) => ({
|
||||||
|
item,
|
||||||
|
rate,
|
||||||
|
quantity,
|
||||||
|
fromLocation: toLocation,
|
||||||
|
toLocation: fromLocation,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await this.validateTransfers(reverseTransferDetails);
|
||||||
|
}
|
||||||
|
|
||||||
async #sync() {
|
async #sync() {
|
||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
await item.sync();
|
await item.sync();
|
||||||
@ -117,15 +131,21 @@ export class StockManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const quantityBefore =
|
const date = details.date.toISOString();
|
||||||
|
let quantityBefore =
|
||||||
(await this.fyo.db.getStockQuantity(
|
(await this.fyo.db.getStockQuantity(
|
||||||
details.item,
|
details.item,
|
||||||
details.fromLocation,
|
details.fromLocation,
|
||||||
undefined,
|
undefined,
|
||||||
details.date.toISOString()
|
date
|
||||||
)) ?? 0;
|
)) ?? 0;
|
||||||
|
|
||||||
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
const formattedDate = this.fyo.format(details.date, 'Datetime');
|
||||||
|
|
||||||
|
if (this.isCancelled) {
|
||||||
|
quantityBefore += details.quantity;
|
||||||
|
}
|
||||||
|
|
||||||
if (quantityBefore < details.quantity) {
|
if (quantityBefore < details.quantity) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
[
|
[
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { Doc } from 'fyo/model/doc';
|
|
||||||
import {
|
import {
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
FiltersMap,
|
FiltersMap,
|
||||||
FormulaMap,
|
FormulaMap,
|
||||||
ListViewSettings
|
ListViewSettings,
|
||||||
} from 'fyo/model/types';
|
} from 'fyo/model/types';
|
||||||
import { getDocStatusListColumn } from 'models/helpers';
|
import { getDocStatusListColumn } from 'models/helpers';
|
||||||
|
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { StockManager } from './StockManager';
|
|
||||||
import { StockMovementItem } from './StockMovementItem';
|
import { StockMovementItem } from './StockMovementItem';
|
||||||
|
import { Transfer } from './Transfer';
|
||||||
import { MovementType } from './types';
|
import { MovementType } from './types';
|
||||||
|
|
||||||
export class StockMovement extends Doc {
|
export class StockMovement extends Transfer {
|
||||||
name?: string;
|
name?: string;
|
||||||
date?: Date;
|
date?: Date;
|
||||||
numberSeries?: string;
|
numberSeries?: string;
|
||||||
@ -20,6 +20,14 @@ export class StockMovement extends Doc {
|
|||||||
items?: StockMovementItem[];
|
items?: StockMovementItem[];
|
||||||
amount?: Money;
|
amount?: Money;
|
||||||
|
|
||||||
|
override get isTransactional(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async getPosting(): Promise<LedgerPosting | null> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
formulas: FormulaMap = {
|
formulas: FormulaMap = {
|
||||||
amount: {
|
amount: {
|
||||||
formula: () => {
|
formula: () => {
|
||||||
@ -46,23 +54,6 @@ export class StockMovement extends Doc {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeSubmit(): Promise<void> {
|
|
||||||
await super.beforeSubmit();
|
|
||||||
const transferDetails = this._getTransferDetails();
|
|
||||||
await this._getStockManager().validateTransfers(transferDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
async afterSubmit(): Promise<void> {
|
|
||||||
await super.afterSubmit();
|
|
||||||
const transferDetails = this._getTransferDetails();
|
|
||||||
await this._getStockManager().createTransfers(transferDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
async afterCancel(): Promise<void> {
|
|
||||||
await super.afterCancel();
|
|
||||||
await this._getStockManager().cancelTransfers();
|
|
||||||
}
|
|
||||||
|
|
||||||
_getTransferDetails() {
|
_getTransferDetails() {
|
||||||
return (this.items ?? []).map((row) => ({
|
return (this.items ?? []).map((row) => ({
|
||||||
item: row.item!,
|
item: row.item!,
|
||||||
@ -72,16 +63,4 @@ export class StockMovement extends Doc {
|
|||||||
toLocation: row.toLocation,
|
toLocation: row.toLocation,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_getStockManager(): StockManager {
|
|
||||||
return new StockManager(
|
|
||||||
{
|
|
||||||
date: this.date!,
|
|
||||||
referenceName: this.name!,
|
|
||||||
referenceType: this.schemaName,
|
|
||||||
},
|
|
||||||
this.isCancelled,
|
|
||||||
this.fyo
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,136 @@
|
|||||||
|
import { t } from 'fyo';
|
||||||
import { Attachment } from 'fyo/core/types';
|
import { Attachment } from 'fyo/core/types';
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
|
import { DefaultMap, FiltersMap, FormulaMap } from 'fyo/model/types';
|
||||||
|
import { NotFoundError, ValidationError } from 'fyo/utils/errors';
|
||||||
|
import { Defaults } from 'models/baseModels/Defaults/Defaults';
|
||||||
|
import { getNumberSeries } from 'models/helpers';
|
||||||
|
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
|
||||||
|
import { ModelNameEnum } from 'models/types';
|
||||||
|
import { Money } from 'pesa';
|
||||||
|
import { StockTransferItem } from './StockTransferItem';
|
||||||
|
import { Transfer } from './Transfer';
|
||||||
|
|
||||||
export abstract class StockTransfer extends Doc {
|
export abstract class StockTransfer extends Transfer {
|
||||||
name?: string;
|
name?: string;
|
||||||
date?: string;
|
date?: Date;
|
||||||
party?: string;
|
party?: string;
|
||||||
terms?: string;
|
terms?: string;
|
||||||
attachment?: Attachment;
|
attachment?: Attachment;
|
||||||
|
grandTotal?: Money;
|
||||||
|
items?: StockTransferItem[];
|
||||||
|
|
||||||
|
get isSales() {
|
||||||
|
return this.schemaName === ModelNameEnum.Shipment;
|
||||||
|
}
|
||||||
|
|
||||||
|
formulas: FormulaMap = {
|
||||||
|
grandTotal: {
|
||||||
|
formula: () => this.getSum('items', 'amount', false),
|
||||||
|
dependsOn: ['items'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaults: DefaultMap = {
|
||||||
|
numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo),
|
||||||
|
terms: (doc) => {
|
||||||
|
const defaults = doc.fyo.singles.Defaults as Defaults | undefined;
|
||||||
|
if (doc.schemaName === ModelNameEnum.Shipment) {
|
||||||
|
return defaults?.shipmentTerms ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaults?.purchaseReceiptTerms ?? '';
|
||||||
|
},
|
||||||
|
date: () => new Date().toISOString().slice(0, 10),
|
||||||
|
};
|
||||||
|
|
||||||
|
static filters: FiltersMap = {
|
||||||
|
party: (doc: Doc) => ({
|
||||||
|
role: ['in', [doc.isSales ? 'Customer' : 'Supplier', 'Both']],
|
||||||
|
}),
|
||||||
|
numberSeries: (doc: Doc) => ({ referenceType: doc.schemaName }),
|
||||||
|
};
|
||||||
|
|
||||||
|
override _getTransferDetails() {
|
||||||
|
return (this.items ?? []).map((row) => {
|
||||||
|
let fromLocation = undefined;
|
||||||
|
let toLocation = undefined;
|
||||||
|
|
||||||
|
if (this.isSales) {
|
||||||
|
fromLocation = row.location;
|
||||||
|
} else {
|
||||||
|
toLocation = row.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
item: row.item!,
|
||||||
|
rate: row.rate!,
|
||||||
|
quantity: row.quantity!,
|
||||||
|
fromLocation,
|
||||||
|
toLocation,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override async getPosting(): Promise<LedgerPosting | null> {
|
||||||
|
await this.validateAccounts();
|
||||||
|
const stockInHand = (await this.fyo.getValue(
|
||||||
|
ModelNameEnum.InventorySettings,
|
||||||
|
'stockInHand'
|
||||||
|
)) as string;
|
||||||
|
|
||||||
|
const amount = this.grandTotal ?? this.fyo.pesa(0);
|
||||||
|
const posting = new LedgerPosting(this, this.fyo);
|
||||||
|
|
||||||
|
if (this.isSales) {
|
||||||
|
const costOfGoodsSold = (await this.fyo.getValue(
|
||||||
|
ModelNameEnum.InventorySettings,
|
||||||
|
'costOfGoodsSold'
|
||||||
|
)) as string;
|
||||||
|
|
||||||
|
await posting.debit(costOfGoodsSold, amount);
|
||||||
|
await posting.credit(stockInHand, amount);
|
||||||
|
} else {
|
||||||
|
const stockReceivedButNotBilled = (await this.fyo.getValue(
|
||||||
|
ModelNameEnum.InventorySettings,
|
||||||
|
'stockReceivedButNotBilled'
|
||||||
|
)) as string;
|
||||||
|
|
||||||
|
await posting.debit(stockInHand, amount);
|
||||||
|
await posting.credit(stockReceivedButNotBilled, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
await posting.makeRoundOffEntry()
|
||||||
|
return posting;
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateAccounts() {
|
||||||
|
const settings: string[] = ['stockInHand'];
|
||||||
|
if (this.isSales) {
|
||||||
|
settings.push('costOfGoodsSold');
|
||||||
|
} else {
|
||||||
|
settings.push('stockReceivedButNotBilled');
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages: string[] = [];
|
||||||
|
for (const setting of settings) {
|
||||||
|
const value = this.fyo.singles.InventorySettings?.[setting] as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
|
const field = this.fyo.getField(ModelNameEnum.InventorySettings, setting);
|
||||||
|
if (!value) {
|
||||||
|
messages.push(t`${field.label} account not set in Inventory Settings.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = await this.fyo.db.exists(ModelNameEnum.Account, value);
|
||||||
|
if (!exists) {
|
||||||
|
messages.push(t`Account ${value} does not exist.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.length) {
|
||||||
|
throw new ValidationError(messages.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
|
import { FiltersMap, FormulaMap } from 'fyo/model/types';
|
||||||
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
|
import { locationFilter } from './helpers';
|
||||||
|
|
||||||
export class StockTransferItem extends Doc {
|
export class StockTransferItem extends Doc {
|
||||||
item?: string;
|
item?: string;
|
||||||
@ -10,4 +13,98 @@ export class StockTransferItem extends Doc {
|
|||||||
unit?: string;
|
unit?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
hsnCode?: number;
|
hsnCode?: number;
|
||||||
|
|
||||||
|
formulas: FormulaMap = {
|
||||||
|
description: {
|
||||||
|
formula: async () =>
|
||||||
|
(await this.fyo.getValue(
|
||||||
|
'Item',
|
||||||
|
this.item as string,
|
||||||
|
'description'
|
||||||
|
)) as string,
|
||||||
|
dependsOn: ['item'],
|
||||||
|
},
|
||||||
|
unit: {
|
||||||
|
formula: async () =>
|
||||||
|
(await this.fyo.getValue(
|
||||||
|
'Item',
|
||||||
|
this.item as string,
|
||||||
|
'unit'
|
||||||
|
)) as string,
|
||||||
|
dependsOn: ['item'],
|
||||||
|
},
|
||||||
|
hsnCode: {
|
||||||
|
formula: async () =>
|
||||||
|
(await this.fyo.getValue(
|
||||||
|
'Item',
|
||||||
|
this.item as string,
|
||||||
|
'hsnCode'
|
||||||
|
)) as string,
|
||||||
|
dependsOn: ['item'],
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
formula: () => {
|
||||||
|
return this.rate?.mul(this.quantity ?? 0) ?? this.fyo.pesa(0);
|
||||||
|
},
|
||||||
|
dependsOn: ['rate', 'quantity'],
|
||||||
|
},
|
||||||
|
rate: {
|
||||||
|
formula: async (fieldname) => {
|
||||||
|
const rate = (await this.fyo.getValue(
|
||||||
|
'Item',
|
||||||
|
this.item as string,
|
||||||
|
'rate'
|
||||||
|
)) as undefined | Money;
|
||||||
|
|
||||||
|
if (!rate?.float && this.rate?.float) {
|
||||||
|
return this.rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rate ?? this.fyo.pesa(0);
|
||||||
|
},
|
||||||
|
dependsOn: ['item'],
|
||||||
|
},
|
||||||
|
quantity: {
|
||||||
|
formula: async () => {
|
||||||
|
if (!this.item) {
|
||||||
|
return this.quantity as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemDoc = await this.fyo.doc.getDoc(
|
||||||
|
ModelNameEnum.Item,
|
||||||
|
this.item as string
|
||||||
|
);
|
||||||
|
|
||||||
|
const unitDoc = itemDoc.getLink('unit');
|
||||||
|
if (unitDoc?.isWhole) {
|
||||||
|
return Math.round(this.quantity as number);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.quantity as number;
|
||||||
|
},
|
||||||
|
dependsOn: ['quantity'],
|
||||||
|
},
|
||||||
|
account: {
|
||||||
|
formula: () => {
|
||||||
|
let accountType = 'expenseAccount';
|
||||||
|
if (this.isSales) {
|
||||||
|
accountType = 'incomeAccount';
|
||||||
|
}
|
||||||
|
return this.fyo.getValue('Item', this.item as string, accountType);
|
||||||
|
},
|
||||||
|
dependsOn: ['item'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static filters: FiltersMap = {
|
||||||
|
item: (doc: Doc) => {
|
||||||
|
let itemNotFor = 'Sales';
|
||||||
|
if (doc.isSales) {
|
||||||
|
itemNotFor = 'Purchases';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { for: ['not in', [itemNotFor]], trackItem: true };
|
||||||
|
},
|
||||||
|
location: locationFilter,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
51
models/inventory/Transfer.ts
Normal file
51
models/inventory/Transfer.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Transactional } from 'models/Transactional/Transactional';
|
||||||
|
import { StockManager } from './StockManager';
|
||||||
|
import { SMTransferDetails } from './types';
|
||||||
|
|
||||||
|
export abstract class Transfer extends Transactional {
|
||||||
|
date?: Date;
|
||||||
|
|
||||||
|
async beforeSubmit(): Promise<void> {
|
||||||
|
await super.beforeSubmit();
|
||||||
|
const transferDetails = this._getTransferDetails();
|
||||||
|
await this._getStockManager().validateTransfers(transferDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
async afterSubmit(): Promise<void> {
|
||||||
|
await super.afterSubmit();
|
||||||
|
const transferDetails = this._getTransferDetails();
|
||||||
|
await this._getStockManager().createTransfers(transferDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
async beforeCancel(): Promise<void> {
|
||||||
|
await super.beforeCancel();
|
||||||
|
const transferDetails = this._getTransferDetails();
|
||||||
|
const stockManager = this._getStockManager();
|
||||||
|
stockManager.isCancelled = true;
|
||||||
|
await stockManager.validateCancel(transferDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
async afterCancel(): Promise<void> {
|
||||||
|
await super.afterCancel();
|
||||||
|
await this._getStockManager().cancelTransfers();
|
||||||
|
}
|
||||||
|
|
||||||
|
_getStockManager(): StockManager {
|
||||||
|
let date = this.date!;
|
||||||
|
if (typeof date === 'string') {
|
||||||
|
date = new Date(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StockManager(
|
||||||
|
{
|
||||||
|
date,
|
||||||
|
referenceName: this.name!,
|
||||||
|
referenceType: this.schemaName,
|
||||||
|
},
|
||||||
|
this.isCancelled,
|
||||||
|
this.fyo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract _getTransferDetails(): SMTransferDetails[];
|
||||||
|
}
|
@ -1,8 +1,18 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { StockMovement } from '../StockMovement';
|
import { StockMovement } from '../StockMovement';
|
||||||
|
import { StockTransfer } from '../StockTransfer';
|
||||||
import { MovementType } from '../types';
|
import { MovementType } from '../types';
|
||||||
|
|
||||||
|
type ALE = {
|
||||||
|
date: string;
|
||||||
|
account: string;
|
||||||
|
party: string;
|
||||||
|
debit: string;
|
||||||
|
credit: string;
|
||||||
|
reverted: number;
|
||||||
|
};
|
||||||
|
|
||||||
type SLE = {
|
type SLE = {
|
||||||
date: string;
|
date: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -20,10 +30,28 @@ type Transfer = {
|
|||||||
rate: number;
|
rate: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface TransferTwo extends Omit<Transfer, 'from' | 'to'> {
|
||||||
|
location: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function getItem(name: string, rate: number) {
|
export function getItem(name: string, rate: number) {
|
||||||
return { name, rate, trackItem: true };
|
return { name, rate, trackItem: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStockTransfer(
|
||||||
|
schemaName: ModelNameEnum.PurchaseReceipt | ModelNameEnum.Shipment,
|
||||||
|
party: string,
|
||||||
|
date: Date,
|
||||||
|
transfers: TransferTwo[],
|
||||||
|
fyo: Fyo
|
||||||
|
): Promise<StockTransfer> {
|
||||||
|
const doc = fyo.doc.getNewDoc(schemaName, { party, date }) as StockTransfer;
|
||||||
|
for (const { item, location, quantity, rate } of transfers) {
|
||||||
|
await doc.append('items', { item, location, quantity, rate });
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getStockMovement(
|
export async function getStockMovement(
|
||||||
movementType: MovementType,
|
movementType: MovementType,
|
||||||
date: Date,
|
date: Date,
|
||||||
@ -64,3 +92,14 @@ export async function getSLEs(
|
|||||||
fields: ['date', 'name', 'item', 'location', 'rate', 'quantity'],
|
fields: ['date', 'name', 'item', 'location', 'rate', 'quantity'],
|
||||||
})) as SLE[];
|
})) as SLE[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getALEs(
|
||||||
|
referenceName: string,
|
||||||
|
referenceType: string,
|
||||||
|
fyo: Fyo
|
||||||
|
) {
|
||||||
|
return (await fyo.db.getAllRaw(ModelNameEnum.AccountingLedgerEntry, {
|
||||||
|
filters: { referenceName, referenceType },
|
||||||
|
fields: ['date', 'account', 'party', 'debit', 'credit', 'reverted'],
|
||||||
|
})) as ALE[];
|
||||||
|
}
|
||||||
|
288
models/inventory/tests/testStockTransfer.spec.ts
Normal file
288
models/inventory/tests/testStockTransfer.spec.ts
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import {
|
||||||
|
assertDoesNotThrow,
|
||||||
|
assertThrows
|
||||||
|
} from 'backend/database/tests/helpers';
|
||||||
|
import { ModelNameEnum } from 'models/types';
|
||||||
|
import { RawValue } from 'schemas/types';
|
||||||
|
import test from 'tape';
|
||||||
|
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
||||||
|
import { InventorySettings } from '../InventorySettings';
|
||||||
|
import { ValuationMethod } from '../types';
|
||||||
|
import { getALEs, getItem, getSLEs, getStockTransfer } from './helpers';
|
||||||
|
|
||||||
|
const fyo = getTestFyo();
|
||||||
|
setupTestFyo(fyo, __filename);
|
||||||
|
|
||||||
|
const item = 'Pen';
|
||||||
|
const location = 'Common';
|
||||||
|
const party = 'Someone';
|
||||||
|
const testDocs = {
|
||||||
|
Item: {
|
||||||
|
[item]: getItem(item, 100),
|
||||||
|
},
|
||||||
|
Location: {
|
||||||
|
[location]: { name: location },
|
||||||
|
},
|
||||||
|
Party: { [party]: { name: party, Role: 'Both' } },
|
||||||
|
} as Record<string, Record<string, { name: string; [key: string]: RawValue }>>;
|
||||||
|
|
||||||
|
test('insert test docs', async (t) => {
|
||||||
|
for (const schemaName in testDocs) {
|
||||||
|
for (const name in testDocs[schemaName]) {
|
||||||
|
await fyo.doc.getNewDoc(schemaName, testDocs[schemaName][name]).sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ok(await fyo.db.exists(ModelNameEnum.Party, party), 'party created');
|
||||||
|
t.ok(
|
||||||
|
await fyo.db.exists(ModelNameEnum.Location, location),
|
||||||
|
'location created'
|
||||||
|
);
|
||||||
|
t.ok(await fyo.db.exists(ModelNameEnum.Item, item), 'item created');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inventory settings', async (t) => {
|
||||||
|
const doc = (await fyo.doc.getDoc(
|
||||||
|
ModelNameEnum.InventorySettings
|
||||||
|
)) as InventorySettings;
|
||||||
|
|
||||||
|
t.equal(doc.valuationMethod, ValuationMethod.FIFO, 'fifo valuation set');
|
||||||
|
t.ok(doc.stockInHand, 'stock in hand set');
|
||||||
|
t.ok(doc.stockReceivedButNotBilled, 'stock rec. but not billed set');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('PurchaseReceipt, create inward stock movement', async (t) => {
|
||||||
|
const date = new Date('2022-01-01');
|
||||||
|
const rate = (testDocs['Item'][item].rate as number) ?? 0;
|
||||||
|
const quantity = 10;
|
||||||
|
const doc = await getStockTransfer(
|
||||||
|
ModelNameEnum.PurchaseReceipt,
|
||||||
|
party,
|
||||||
|
date,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
item,
|
||||||
|
location,
|
||||||
|
quantity,
|
||||||
|
rate,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fyo
|
||||||
|
);
|
||||||
|
|
||||||
|
await doc.sync();
|
||||||
|
const grandTotal = quantity * rate;
|
||||||
|
t.equal(doc.grandTotal?.float, quantity * rate);
|
||||||
|
|
||||||
|
await doc.submit();
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
(await fyo.db.getAllRaw(ModelNameEnum.PurchaseReceipt)).length,
|
||||||
|
1,
|
||||||
|
'purchase receipt created'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
(await getSLEs(doc.name!, doc.schemaName, fyo)).length,
|
||||||
|
1,
|
||||||
|
'sle created'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
await fyo.db.getStockQuantity(item, location),
|
||||||
|
quantity,
|
||||||
|
'stock purchased'
|
||||||
|
);
|
||||||
|
t.ok(doc.name?.startsWith('PREC-'));
|
||||||
|
|
||||||
|
const ales = await getALEs(doc.name!, doc.schemaName, fyo);
|
||||||
|
for (const ale of ales) {
|
||||||
|
t.equal(ale.party, party, 'party matches');
|
||||||
|
if (ale.account === 'Stock Received But Not Billed') {
|
||||||
|
t.equal(parseFloat(ale.debit), 0);
|
||||||
|
t.equal(parseFloat(ale.credit), grandTotal);
|
||||||
|
} else {
|
||||||
|
t.equal(parseFloat(ale.credit), 0);
|
||||||
|
t.equal(parseFloat(ale.debit), grandTotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Shipment, create outward stock movement', async (t) => {
|
||||||
|
const date = new Date('2022-01-02');
|
||||||
|
const rate = (testDocs['Item'][item].rate as number) ?? 0;
|
||||||
|
const quantity = 5;
|
||||||
|
const doc = await getStockTransfer(
|
||||||
|
ModelNameEnum.Shipment,
|
||||||
|
party,
|
||||||
|
date,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
item,
|
||||||
|
location,
|
||||||
|
quantity,
|
||||||
|
rate,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fyo
|
||||||
|
);
|
||||||
|
|
||||||
|
await doc.sync();
|
||||||
|
const grandTotal = quantity * rate;
|
||||||
|
t.equal(doc.grandTotal?.float, grandTotal);
|
||||||
|
|
||||||
|
await doc.submit();
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
(await fyo.db.getAllRaw(ModelNameEnum.Shipment)).length,
|
||||||
|
1,
|
||||||
|
'shipment created'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
(await getSLEs(doc.name!, doc.schemaName, fyo)).length,
|
||||||
|
1,
|
||||||
|
'sle created'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
await fyo.db.getStockQuantity(item, location),
|
||||||
|
10 - quantity,
|
||||||
|
'stock purchased'
|
||||||
|
);
|
||||||
|
t.ok(doc.name?.startsWith('SHPM-'));
|
||||||
|
|
||||||
|
const ales = await getALEs(doc.name!, doc.schemaName, fyo);
|
||||||
|
for (const ale of ales) {
|
||||||
|
t.equal(ale.party, party, 'party matches');
|
||||||
|
if (ale.account === 'Cost of Goods Sold') {
|
||||||
|
t.equal(parseFloat(ale.debit), grandTotal);
|
||||||
|
t.equal(parseFloat(ale.credit), 0);
|
||||||
|
} else {
|
||||||
|
t.equal(parseFloat(ale.debit), 0);
|
||||||
|
t.equal(parseFloat(ale.credit), grandTotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Shipment, invalid', async (t) => {
|
||||||
|
const date = new Date('2022-01-03');
|
||||||
|
const rate = (testDocs['Item'][item].rate as number) ?? 0;
|
||||||
|
const quantity = 10;
|
||||||
|
const doc = await getStockTransfer(
|
||||||
|
ModelNameEnum.Shipment,
|
||||||
|
party,
|
||||||
|
date,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
item,
|
||||||
|
location,
|
||||||
|
quantity,
|
||||||
|
rate,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fyo
|
||||||
|
);
|
||||||
|
|
||||||
|
await doc.sync();
|
||||||
|
const grandTotal = quantity * rate;
|
||||||
|
|
||||||
|
t.equal(await fyo.db.getStockQuantity(item, location), 5, 'stock unchanged');
|
||||||
|
t.equal(doc.grandTotal?.float, grandTotal);
|
||||||
|
await assertThrows(async () => await doc.submit());
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
(await getSLEs(doc.name!, doc.schemaName, fyo)).length,
|
||||||
|
0,
|
||||||
|
'sles not created'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
(await getALEs(doc.name!, doc.schemaName, fyo)).length,
|
||||||
|
0,
|
||||||
|
'ales not created'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Stock Transfer, invalid cancellation', async (t) => {
|
||||||
|
const { name } =
|
||||||
|
(
|
||||||
|
(await fyo.db.getAllRaw(ModelNameEnum.PurchaseReceipt)) as {
|
||||||
|
name: string;
|
||||||
|
}[]
|
||||||
|
)[0] ?? {};
|
||||||
|
|
||||||
|
t.ok(name?.startsWith('PREC-'));
|
||||||
|
const doc = await fyo.doc.getDoc(ModelNameEnum.PurchaseReceipt, name);
|
||||||
|
await assertThrows(async () => await doc.cancel());
|
||||||
|
t.equal(await fyo.db.getStockQuantity(item, location), 5, 'stock unchanged');
|
||||||
|
t.equal(
|
||||||
|
(await getSLEs(name, doc.schemaName, fyo)).length,
|
||||||
|
1,
|
||||||
|
'sle unchanged'
|
||||||
|
);
|
||||||
|
const ales = await getALEs(name, doc.schemaName, fyo);
|
||||||
|
t.ok(ales.every((i) => !i.reverted) && ales.length === 2, 'ale unchanged');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Shipment, cancel and delete', async (t) => {
|
||||||
|
const { name } =
|
||||||
|
(
|
||||||
|
(await fyo.db.getAllRaw(ModelNameEnum.Shipment, { order: 'asc' })) as {
|
||||||
|
name: string;
|
||||||
|
}[]
|
||||||
|
)[0] ?? {};
|
||||||
|
|
||||||
|
t.ok(name?.startsWith('SHPM-'), 'number series matches');
|
||||||
|
const doc = await fyo.doc.getDoc(ModelNameEnum.Shipment, name);
|
||||||
|
t.ok(doc.isSubmitted, `doc ${name} is submitted`);
|
||||||
|
await assertDoesNotThrow(async () => await doc.cancel());
|
||||||
|
t.ok(doc.isCancelled), `doc is cancelled`;
|
||||||
|
|
||||||
|
t.equal(await fyo.db.getStockQuantity(item, location), 10, 'stock changed');
|
||||||
|
t.equal((await getSLEs(name, doc.schemaName, fyo)).length, 0, 'sle deleted');
|
||||||
|
const ales = await getALEs(name, doc.schemaName, fyo);
|
||||||
|
t.ok(ales.every((i) => !!i.reverted) && ales.length === 4, 'ale reverted');
|
||||||
|
|
||||||
|
await doc.delete();
|
||||||
|
t.equal((await getALEs(name, doc.schemaName, fyo)).length, 0, 'ales deleted');
|
||||||
|
t.equal(
|
||||||
|
(
|
||||||
|
await fyo.db.getAllRaw(ModelNameEnum.Shipment, {
|
||||||
|
filters: { name: name },
|
||||||
|
})
|
||||||
|
).length,
|
||||||
|
0,
|
||||||
|
'doc deleted'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Purchase Receipt, cancel and delete', async (t) => {
|
||||||
|
const { name } =
|
||||||
|
(
|
||||||
|
(await fyo.db.getAllRaw(ModelNameEnum.PurchaseReceipt, {
|
||||||
|
order: 'asc',
|
||||||
|
})) as {
|
||||||
|
name: string;
|
||||||
|
}[]
|
||||||
|
)[0] ?? {};
|
||||||
|
|
||||||
|
t.ok(name?.startsWith('PREC-'), 'number series matches');
|
||||||
|
const doc = await fyo.doc.getDoc(ModelNameEnum.PurchaseReceipt, name);
|
||||||
|
t.ok(doc.isSubmitted, `doc ${name} is submitted`);
|
||||||
|
await assertDoesNotThrow(async () => await doc.cancel());
|
||||||
|
t.ok(doc.isCancelled), `doc is cancelled`;
|
||||||
|
|
||||||
|
t.equal(await fyo.db.getStockQuantity(item, location), null, 'stock changed');
|
||||||
|
t.equal((await getSLEs(name, doc.schemaName, fyo)).length, 0, 'sle deleted');
|
||||||
|
const ales = await getALEs(name, doc.schemaName, fyo);
|
||||||
|
t.ok(ales.every((i) => !!i.reverted) && ales.length === 4, 'ale reverted');
|
||||||
|
|
||||||
|
await doc.delete();
|
||||||
|
t.equal((await getALEs(name, doc.schemaName, fyo)).length, 0, 'ales deleted');
|
||||||
|
t.equal(
|
||||||
|
(
|
||||||
|
await fyo.db.getAllRaw(ModelNameEnum.Shipment, {
|
||||||
|
filters: { name: name },
|
||||||
|
})
|
||||||
|
).length,
|
||||||
|
0,
|
||||||
|
'doc deleted'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
closeTestFyo(fyo, __filename);
|
@ -1,5 +1,10 @@
|
|||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
|
|
||||||
|
export enum ValuationMethod {
|
||||||
|
'FIFO' = 'FIFO',
|
||||||
|
'MovingAverage' = 'MovingAverage',
|
||||||
|
}
|
||||||
|
|
||||||
export enum MovementType {
|
export enum MovementType {
|
||||||
'MaterialIssue' = 'MaterialIssue',
|
'MaterialIssue' = 'MaterialIssue',
|
||||||
'MaterialReceipt' = 'MaterialReceipt',
|
'MaterialReceipt' = 'MaterialReceipt',
|
||||||
|
@ -94,12 +94,10 @@ export class StockLedger extends Report {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toDate && row.date > toDate) {
|
if (toDate && row.date > toDate) {
|
||||||
console.log('here');
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromDate && row.date < fromDate) {
|
if (fromDate && row.date < fromDate) {
|
||||||
console.log('here');
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,15 +25,19 @@
|
|||||||
"fieldname": "stockInHand",
|
"fieldname": "stockInHand",
|
||||||
"label": "Stock In Hand Acc.",
|
"label": "Stock In Hand Acc.",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"target": "Account",
|
"target": "Account"
|
||||||
"create": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "stockReceivedButNotBilled",
|
"fieldname": "stockReceivedButNotBilled",
|
||||||
"label": "Stock Received But Not Billed Acc.",
|
"label": "Stock Received But Not Billed Acc.",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"target": "Account",
|
"target": "Account"
|
||||||
"create": true
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "costOfGoodsSold",
|
||||||
|
"label": "Cost Of Goods Sold",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,6 @@ const components = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'FormControl',
|
name: 'FormControl',
|
||||||
render() {
|
render() {
|
||||||
if (!this.$attrs.df) {
|
|
||||||
console.log(this);
|
|
||||||
}
|
|
||||||
const fieldtype = this.$attrs.df.fieldtype;
|
const fieldtype = this.$attrs.df.fieldtype;
|
||||||
const component = components[fieldtype] ?? Data;
|
const component = components[fieldtype] ?? Data;
|
||||||
|
|
||||||
|
@ -52,10 +52,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fields() {
|
fields() {
|
||||||
console.log(
|
|
||||||
'changed',
|
|
||||||
this.doc?.schema.fields.map(({ fieldname }) => fieldname).join(',')
|
|
||||||
);
|
|
||||||
return this.doc?.schema.fields;
|
return this.doc?.schema.fields;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { AccountingSettings } from 'models/baseModels/AccountingSettings/AccountingSettings';
|
import { AccountingSettings } from 'models/baseModels/AccountingSettings/AccountingSettings';
|
||||||
import { numberSeriesDefaultsMap } from 'models/baseModels/Defaults/Defaults';
|
import { numberSeriesDefaultsMap } from 'models/baseModels/Defaults/Defaults';
|
||||||
import { InventorySettings } from 'models/inventory/InventorySettings';
|
import { InventorySettings } from 'models/inventory/InventorySettings';
|
||||||
import { valuationMethod } from 'models/inventory/types';
|
import { ValuationMethod } from 'models/inventory/types';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { createRegionalRecords } from 'src/regional';
|
import { createRegionalRecords } from 'src/regional';
|
||||||
import {
|
import {
|
||||||
@ -346,32 +346,26 @@ async function updateInventorySettings(fyo: Fyo) {
|
|||||||
)) as InventorySettings;
|
)) as InventorySettings;
|
||||||
|
|
||||||
if (!inventorySettings.valuationMethod) {
|
if (!inventorySettings.valuationMethod) {
|
||||||
await inventorySettings.set('valuationMethod', valuationMethod.FIFO);
|
await inventorySettings.set('valuationMethod', ValuationMethod.FIFO);
|
||||||
}
|
}
|
||||||
|
const accountTypeDefaultMap = {
|
||||||
|
[AccountTypeEnum.Stock]: 'stockInHand',
|
||||||
|
[AccountTypeEnum['Stock Received But Not Billed']]:
|
||||||
|
'stockReceivedButNotBilled',
|
||||||
|
[AccountTypeEnum['Cost of Goods Sold']]: 'costOfGoodsSold',
|
||||||
|
} as Record<string, string>;
|
||||||
|
|
||||||
const stockAccounts = (await fyo.db.getAllRaw('Account', {
|
for (const accountType in accountTypeDefaultMap) {
|
||||||
filters: { accountType: AccountTypeEnum.Stock, isGroup: false },
|
const accounts = (await fyo.db.getAllRaw('Account', {
|
||||||
})) as { name: string }[];
|
filters: { accountType, isGroup: false },
|
||||||
|
})) as { name: string }[];
|
||||||
|
|
||||||
if (stockAccounts.length && !inventorySettings.stockInHand) {
|
if (!accounts.length) {
|
||||||
await inventorySettings.set('stockInHand', stockAccounts[0].name);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stockReceivedButNotBilled = (await fyo.db.getAllRaw('Account', {
|
const settingName = accountTypeDefaultMap[accountType]!;
|
||||||
filters: {
|
inventorySettings.set(settingName, accounts[0].name);
|
||||||
accountType: AccountTypeEnum['Stock Received But Not Billed'],
|
|
||||||
isGroup: false,
|
|
||||||
},
|
|
||||||
})) as { name: string }[];
|
|
||||||
|
|
||||||
if (
|
|
||||||
stockReceivedButNotBilled.length &&
|
|
||||||
!inventorySettings.stockInReceivedButNotBilled
|
|
||||||
) {
|
|
||||||
await inventorySettings.set(
|
|
||||||
'stockInReceivedButNotBilled',
|
|
||||||
stockAccounts[0].name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await inventorySettings.sync();
|
await inventorySettings.sync();
|
||||||
|
Loading…
Reference in New Issue
Block a user