2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 11:29:00 +00:00
books/models/inventory/StockTransfer.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

137 lines
3.8 KiB
TypeScript
Raw Normal View History

import { t } from 'fyo';
import { Attachment } from 'fyo/core/types';
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 Transfer {
name?: string;
date?: Date;
party?: string;
terms?: string;
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(' '));
}
}
}