mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-24 00:29:49 +00:00
- fix: Schedule write journal entries after item compute cost.
- fix: active vouchers query. - fix: remove babel loader in server-side.
This commit is contained in:
@@ -58,6 +58,7 @@
|
|||||||
"nodemon": "^1.19.1",
|
"nodemon": "^1.19.1",
|
||||||
"objection": "^2.0.10",
|
"objection": "^2.0.10",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"ts-transformer-keys": "^0.4.2",
|
||||||
"tsyringe": "^4.3.0",
|
"tsyringe": "^4.3.0",
|
||||||
"uniqid": "^5.2.0",
|
"uniqid": "^5.2.0",
|
||||||
"winston": "^3.2.1"
|
"winston": "^3.2.1"
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ export default class ItemsController {
|
|||||||
check('type').exists().trim().escape()
|
check('type').exists().trim().escape()
|
||||||
.isIn(['service', 'non-inventory', 'inventory']),
|
.isIn(['service', 'non-inventory', 'inventory']),
|
||||||
check('sku').optional({ nullable: true }).trim().escape(),
|
check('sku').optional({ nullable: true }).trim().escape(),
|
||||||
|
|
||||||
// Purchase attributes.
|
// Purchase attributes.
|
||||||
check('purchasable').optional().isBoolean().toBoolean(),
|
check('purchasable').optional().isBoolean().toBoolean(),
|
||||||
check('cost_price')
|
check('cost_price')
|
||||||
@@ -92,7 +91,6 @@ export default class ItemsController {
|
|||||||
.exists()
|
.exists()
|
||||||
.isInt()
|
.isInt()
|
||||||
.toInt(),
|
.toInt(),
|
||||||
|
|
||||||
// Sell attributes.
|
// Sell attributes.
|
||||||
check('sellable').optional().isBoolean().toBoolean(),
|
check('sellable').optional().isBoolean().toBoolean(),
|
||||||
check('sell_price')
|
check('sell_price')
|
||||||
@@ -105,7 +103,6 @@ export default class ItemsController {
|
|||||||
.exists()
|
.exists()
|
||||||
.isInt()
|
.isInt()
|
||||||
.toInt(),
|
.toInt(),
|
||||||
|
|
||||||
check('inventory_account_id')
|
check('inventory_account_id')
|
||||||
.if(check('type').equals('inventory'))
|
.if(check('type').equals('inventory'))
|
||||||
.exists()
|
.exists()
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
|
|
||||||
|
|
||||||
export interface ISaleInvoice {
|
export interface ISaleInvoice {
|
||||||
id: number,
|
id: number,
|
||||||
balance: number,
|
balance: number,
|
||||||
|
paymentAmount: number,
|
||||||
invoiceDate: Date,
|
invoiceDate: Date,
|
||||||
entries: [],
|
dueDate: Date,
|
||||||
|
entries: any[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISaleInvoiceOTD {
|
||||||
|
invoiceDate: Date,
|
||||||
|
dueDate: Date,
|
||||||
|
referenceNo: string,
|
||||||
|
invoiceMessage: string,
|
||||||
|
termsConditions: string,
|
||||||
|
entries: any[],
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,10 @@ import {
|
|||||||
IVoucherPaymentMethod,
|
IVoucherPaymentMethod,
|
||||||
IPaymentContext,
|
IPaymentContext,
|
||||||
} from './Payment';
|
} from './Payment';
|
||||||
|
import {
|
||||||
|
ISaleInvoice,
|
||||||
|
ISaleInvoiceOTD,
|
||||||
|
} from './SaleInvoice';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
IBillPaymentEntry,
|
IBillPaymentEntry,
|
||||||
@@ -31,4 +35,7 @@ export {
|
|||||||
IPaymentContext,
|
IPaymentContext,
|
||||||
IVoucherPaymentModel,
|
IVoucherPaymentModel,
|
||||||
IVoucherPaymentMethod,
|
IVoucherPaymentMethod,
|
||||||
|
|
||||||
|
ISaleInvoice,
|
||||||
|
ISaleInvoiceOTD,
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,26 @@
|
|||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
import moment from 'moment';
|
||||||
import InventoryService from '@/services/Inventory/Inventory';
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
|
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
||||||
|
|
||||||
export default class ComputeItemCostJob {
|
export default class ComputeItemCostJob {
|
||||||
|
depends: number;
|
||||||
|
agenda: any;
|
||||||
|
startingDate: Date;
|
||||||
|
|
||||||
|
constructor(agenda) {
|
||||||
|
this.agenda = agenda;
|
||||||
|
this.depends = 0;
|
||||||
|
this.startingDate = null;
|
||||||
|
|
||||||
|
this.agenda.on('complete:compute-item-cost', this.onJobFinished.bind(this));
|
||||||
|
this.agenda.on('start:compute-item-cost', this.onJobStart.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The job handler.
|
||||||
|
* @param {} -
|
||||||
|
*/
|
||||||
public async handler(job, done: Function): Promise<void> {
|
public async handler(job, done: Function): Promise<void> {
|
||||||
const Logger = Container.get('logger');
|
const Logger = Container.get('logger');
|
||||||
const { startingDate, itemId, costMethod = 'FIFO' } = job.attrs.data;
|
const { startingDate, itemId, costMethod = 'FIFO' } = job.attrs.data;
|
||||||
@@ -17,6 +36,38 @@ export default class ComputeItemCostJob {
|
|||||||
Logger.error(`Compute item cost: ${job.attrs.data}, error: ${e}`);
|
Logger.error(`Compute item cost: ${job.attrs.data}, error: ${e}`);
|
||||||
done(e);
|
done(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the job started.
|
||||||
|
* @param {Job} job - .
|
||||||
|
*/
|
||||||
|
async onJobStart(job) {
|
||||||
|
const { startingDate } = job.attrs.data;
|
||||||
|
this.depends += 1;
|
||||||
|
|
||||||
|
if (!this.startingDate || moment(this.startingDate).isBefore(startingDate)) {
|
||||||
|
this.startingDate = startingDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle job complete items cost finished.
|
||||||
|
* @param {Job} job -
|
||||||
|
*/
|
||||||
|
async onJobFinished() {
|
||||||
|
const agenda = Container.get('agenda');
|
||||||
|
const startingDate = this.startingDate;
|
||||||
|
this.depends = Math.max(this.depends - 1, 0);
|
||||||
|
|
||||||
|
console.log(startingDate);
|
||||||
|
|
||||||
|
if (this.depends === 0) {
|
||||||
|
this.startingDate = null;
|
||||||
|
|
||||||
|
await agenda.now('rewrite-invoices-journal-entries', {
|
||||||
|
startingDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
|||||||
agenda.define(
|
agenda.define(
|
||||||
'compute-item-cost',
|
'compute-item-cost',
|
||||||
{ priority: 'high', concurrency: 20 },
|
{ priority: 'high', concurrency: 20 },
|
||||||
new ComputeItemCost().handler,
|
new ComputeItemCost(agenda).handler,
|
||||||
);
|
);
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'rewrite-invoices-journal-entries',
|
'rewrite-invoices-journal-entries',
|
||||||
{ priority: 'normal', concurrency: 1, },
|
{ priority: 'normal', concurrency: 1, },
|
||||||
new RewriteInvoicesJournalEntries().handler,
|
new RewriteInvoicesJournalEntries(agenda).handler,
|
||||||
);
|
);
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'send-voucher-via-phone',
|
'send-voucher-via-phone',
|
||||||
|
|||||||
@@ -84,8 +84,7 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme
|
|||||||
*/
|
*/
|
||||||
private async fetchInvINTransactions() {
|
private async fetchInvINTransactions() {
|
||||||
const commonBuilder = (builder: any) => {
|
const commonBuilder = (builder: any) => {
|
||||||
builder.where('direction', 'IN');
|
builder.orderBy('date', (this.costMethod === 'LIFO') ? 'DESC': 'ASC');
|
||||||
builder.orderBy('date', 'ASC');
|
|
||||||
builder.where('item_id', this.itemId);
|
builder.where('item_id', this.itemId);
|
||||||
};
|
};
|
||||||
const afterInvTransactions: IInventoryTransaction[] =
|
const afterInvTransactions: IInventoryTransaction[] =
|
||||||
@@ -93,8 +92,8 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme
|
|||||||
.query()
|
.query()
|
||||||
.modify('filterDateRange', this.startingDate)
|
.modify('filterDateRange', this.startingDate)
|
||||||
.orderByRaw("FIELD(direction, 'IN', 'OUT')")
|
.orderByRaw("FIELD(direction, 'IN', 'OUT')")
|
||||||
.orderBy('lot_number', (this.costMethod === 'LIFO') ? 'DESC' : 'ASC')
|
|
||||||
.onBuild(commonBuilder)
|
.onBuild(commonBuilder)
|
||||||
|
.orderBy('lot_number', (this.costMethod === 'LIFO') ? 'DESC' : 'ASC')
|
||||||
.withGraphFetched('item');
|
.withGraphFetched('item');
|
||||||
|
|
||||||
const availiableINLots: IInventoryLotCost[] =
|
const availiableINLots: IInventoryLotCost[] =
|
||||||
@@ -102,7 +101,8 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme
|
|||||||
.query()
|
.query()
|
||||||
.modify('filterDateRange', null, this.startingDate)
|
.modify('filterDateRange', null, this.startingDate)
|
||||||
.orderBy('date', 'ASC')
|
.orderBy('date', 'ASC')
|
||||||
.orderBy('lot_number', 'ASC')
|
.where('direction', 'IN')
|
||||||
|
.orderBy('lot_number', 'ASC')
|
||||||
.onBuild(commonBuilder)
|
.onBuild(commonBuilder)
|
||||||
.whereNot('remaining', 0);
|
.whereNot('remaining', 0);
|
||||||
|
|
||||||
|
|||||||
@@ -348,10 +348,18 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {IBill} bill
|
* @param {IBill} bill
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static scheduleComputeBillItemsCost(bill) {
|
static async scheduleComputeBillItemsCost(bill) {
|
||||||
return this.scheduleComputeItemsCost(
|
const billItemsIds = bill.entries.map((entry) => entry.item_id);
|
||||||
bill.entries.map((e) => e.item_id),
|
|
||||||
bill.bill_date,
|
// Retrieves inventory items only.
|
||||||
);
|
const inventoryItems = await Item.tenant().query()
|
||||||
|
.whereIn('id', billItemsIds)
|
||||||
|
.where('type', 'inventory');
|
||||||
|
|
||||||
|
const inventoryItemsIds = inventoryItems.map(i => i.id);
|
||||||
|
|
||||||
|
if (inventoryItemsIds.length > 0) {
|
||||||
|
await this.scheduleComputeItemsCost(inventoryItemsIds, bill.bill_date);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omit, sumBy, difference, pick } from 'lodash';
|
import { omit, sumBy, difference, pick, chain } from 'lodash';
|
||||||
import {
|
import {
|
||||||
SaleInvoice,
|
SaleInvoice,
|
||||||
AccountTransaction,
|
AccountTransaction,
|
||||||
@@ -6,12 +6,14 @@ import {
|
|||||||
Account,
|
Account,
|
||||||
ItemEntry,
|
ItemEntry,
|
||||||
Customer,
|
Customer,
|
||||||
|
Item,
|
||||||
} from '@/models';
|
} from '@/models';
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||||
import InventoryService from '@/services/Inventory/Inventory';
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
||||||
|
import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces';
|
||||||
import { formatDateFields } from '@/utils';
|
import { formatDateFields } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,11 +28,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
* @param {ISaleInvoice}
|
* @param {ISaleInvoice}
|
||||||
* @return {ISaleInvoice}
|
* @return {ISaleInvoice}
|
||||||
*/
|
*/
|
||||||
static async createSaleInvoice(saleInvoiceDTO: any) {
|
static async createSaleInvoice(saleInvoiceDTO: ISaleInvoiceOTD) {
|
||||||
const balance = sumBy(saleInvoiceDTO.entries, 'amount');
|
const balance = sumBy(saleInvoiceDTO.entries, 'amount');
|
||||||
const invLotNumber = await InventoryService.nextLotNumber();
|
const invLotNumber = await InventoryService.nextLotNumber();
|
||||||
const saleInvoice = {
|
const saleInvoice: ISaleInvoice = {
|
||||||
...formatDateFields(saleInvoiceDTO, ['invoice_date', 'due_date']),
|
...formatDateFields(saleInvoiceDTO, ['invoiceDate', 'dueDate']),
|
||||||
balance,
|
balance,
|
||||||
paymentAmount: 0,
|
paymentAmount: 0,
|
||||||
invLotNumber,
|
invLotNumber,
|
||||||
@@ -70,7 +72,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
);
|
);
|
||||||
// Schedule sale invoice re-compute based on the item cost
|
// Schedule sale invoice re-compute based on the item cost
|
||||||
// method and starting date.
|
// method and starting date.
|
||||||
await this.scheduleComputeInvoiceItemsCost(saleInvoice);
|
await this.scheduleComputeInvoiceItemsCost(storedInvoice.id);
|
||||||
|
|
||||||
return storedInvoice;
|
return storedInvoice;
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
balance,
|
balance,
|
||||||
invLotNumber: oldSaleInvoice.invLotNumber,
|
invLotNumber: oldSaleInvoice.invLotNumber,
|
||||||
};
|
};
|
||||||
const updatedSaleInvoices = await SaleInvoice.tenant()
|
const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.tenant()
|
||||||
.query()
|
.query()
|
||||||
.where('id', saleInvoiceId)
|
.where('id', saleInvoiceId)
|
||||||
.update({
|
.update({
|
||||||
@@ -124,10 +126,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
changeCustomerBalanceOper,
|
changeCustomerBalanceOper,
|
||||||
recordInventoryTransOper,
|
recordInventoryTransOper,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Schedule sale invoice re-compute based on the item cost
|
// Schedule sale invoice re-compute based on the item cost
|
||||||
// method and starting date.
|
// method and starting date.
|
||||||
await this.scheduleComputeInvoiceItemsCost(saleInvoice);
|
await this.scheduleComputeInvoiceItemsCost(saleInvoiceId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -313,10 +314,51 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static scheduleComputeInvoiceItemsCost(saleInvoice) {
|
static async scheduleComputeInvoiceItemsCost(saleInvoiceId: number, override?: boolean) {
|
||||||
return this.scheduleComputeItemsCost(
|
const saleInvoice: ISaleInvoice = await SaleInvoice.tenant()
|
||||||
saleInvoice.entries.map((e) => e.item_id),
|
.query()
|
||||||
saleInvoice.invoice_date,
|
.findById(saleInvoiceId)
|
||||||
);
|
.withGraphFetched('entries.item');
|
||||||
|
|
||||||
|
const inventoryItemsIds = chain(saleInvoice.entries)
|
||||||
|
.filter((entry: IItemEntry) => entry.item.type === 'inventory')
|
||||||
|
.map((entry: IItemEntry) => entry.itemId)
|
||||||
|
.uniq().value();
|
||||||
|
|
||||||
|
if (inventoryItemsIds.length === 0) {
|
||||||
|
await this.writeNonInventoryInvoiceJournals(saleInvoice, override);
|
||||||
|
} else {
|
||||||
|
await this.scheduleComputeItemsCost(
|
||||||
|
inventoryItemsIds,
|
||||||
|
saleInvoice.invoice_date,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the sale invoice journal entries.
|
||||||
|
* @param {SaleInvoice} saleInvoice -
|
||||||
|
*/
|
||||||
|
static async writeNonInventoryInvoiceJournals(saleInvoice: ISaleInvoice, override: boolean) {
|
||||||
|
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||||
|
const journal = new JournalPoster(accountsDepGraph);
|
||||||
|
|
||||||
|
if (override) {
|
||||||
|
const oldTransactions = await AccountTransaction.tenant()
|
||||||
|
.query()
|
||||||
|
.where('reference_type', 'SaleInvoice')
|
||||||
|
.where('reference_id', saleInvoice.id)
|
||||||
|
.withGraphFetched('account.type');
|
||||||
|
|
||||||
|
journal.loadEntries(oldTransactions);
|
||||||
|
journal.removeEntries();
|
||||||
|
}
|
||||||
|
this.saleInvoiceJournal(saleInvoice, journal);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
journal.deleteEntries(),
|
||||||
|
journal.saveEntries(),
|
||||||
|
journal.saveBalance(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,31 +9,27 @@ import JournalPoster from '@/services/Accounting/JournalPoster';
|
|||||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||||
import InventoryService from '@/services/Inventory/Inventory';
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
import { ISaleInvoice, IItemEntry, IItem } from '@/interfaces';
|
import { ISaleInvoice, IItemEntry, IItem } from '@/interfaces';
|
||||||
|
import { ISaleInvoice } from '../../interfaces';
|
||||||
|
|
||||||
export default class SaleInvoicesCost {
|
export default class SaleInvoicesCost {
|
||||||
/**
|
/**
|
||||||
* Schedule sale invoice re-compute based on the item
|
* Schedule sale invoice re-compute based on the item
|
||||||
* cost method and starting date.
|
* cost method and starting date.
|
||||||
* @param {number[]} itemIds -
|
* @param {number[]} itemIds - Inventory items ids.
|
||||||
* @param {Date} startingDate -
|
* @param {Date} startingDate - Starting compute cost date.
|
||||||
* @return {Promise<Agenda>}
|
* @return {Promise<Agenda>}
|
||||||
*/
|
*/
|
||||||
static async scheduleComputeItemsCost(itemIds: number[], startingDate: Date) {
|
static async scheduleComputeItemsCost(inventoryItemsIds: number[], startingDate: Date) {
|
||||||
const items: IItem[] = await Item.tenant().query().whereIn('id', itemIds);
|
|
||||||
|
|
||||||
const inventoryItems: IItem[] = items.filter((item: IItem) => item.type === 'inventory');
|
|
||||||
const asyncOpers: Promise<[]>[] = [];
|
const asyncOpers: Promise<[]>[] = [];
|
||||||
|
|
||||||
inventoryItems.forEach((item: IItem) => {
|
inventoryItemsIds.forEach((inventoryItemId: number) => {
|
||||||
const oper: Promise<[]> = InventoryService.scheduleComputeItemCost(
|
const oper: Promise<[]> = InventoryService.scheduleComputeItemCost(
|
||||||
item.id,
|
inventoryItemId,
|
||||||
startingDate,
|
startingDate,
|
||||||
);
|
);
|
||||||
asyncOpers.push(oper);
|
asyncOpers.push(oper);
|
||||||
});
|
});
|
||||||
const writeJEntriesOper: Promise<any> = this.scheduleWriteJournalEntries(startingDate);
|
return Promise.all([...asyncOpers]);
|
||||||
|
|
||||||
return Promise.all([...asyncOpers, writeJEntriesOper]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +59,6 @@ export default class SaleInvoicesCost {
|
|||||||
builder.withGraphFetched('entries.item')
|
builder.withGraphFetched('entries.item')
|
||||||
builder.withGraphFetched('costTransactions(groupedEntriesCost)');
|
builder.withGraphFetched('costTransactions(groupedEntriesCost)');
|
||||||
});
|
});
|
||||||
|
|
||||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||||
const journal = new JournalPoster(accountsDepGraph);
|
const journal = new JournalPoster(accountsDepGraph);
|
||||||
|
|
||||||
@@ -79,62 +74,9 @@ export default class SaleInvoicesCost {
|
|||||||
journal.loadEntries(oldTransactions);
|
journal.loadEntries(oldTransactions);
|
||||||
journal.removeEntries();
|
journal.removeEntries();
|
||||||
}
|
}
|
||||||
const receivableAccount = { id: 10 };
|
|
||||||
|
|
||||||
salesInvoices.forEach((saleInvoice: ISaleInvoice) => {
|
|
||||||
let inventoryTotal: number = 0;
|
|
||||||
const commonEntry = {
|
|
||||||
referenceType: 'SaleInvoice',
|
|
||||||
referenceId: saleInvoice.id,
|
|
||||||
date: saleInvoice.invoiceDate,
|
|
||||||
};
|
|
||||||
const costTransactions: Map<number, number> = new Map(
|
|
||||||
saleInvoice.costTransactions.map((trans: IItemEntry) => [
|
|
||||||
trans.entryId, trans.cost,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
// XXX Debit - Receivable account.
|
|
||||||
const receivableEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: saleInvoice.balance,
|
|
||||||
account: receivableAccount.id,
|
|
||||||
});
|
|
||||||
journal.debit(receivableEntry);
|
|
||||||
|
|
||||||
saleInvoice.entries.forEach((entry: IItemEntry) => {
|
|
||||||
const cost: number = costTransactions.get(entry.id);
|
|
||||||
const income: number = entry.quantity * entry.rate;
|
|
||||||
|
|
||||||
if (entry.item.type === 'inventory' && cost) {
|
salesInvoices.forEach((saleInvoice: ISaleInvoice) => {
|
||||||
// XXX Debit - Cost account.
|
this.saleInvoiceJournal(saleInvoice, journal);
|
||||||
const costEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: cost,
|
|
||||||
account: entry.item.costAccountId,
|
|
||||||
note: entry.description,
|
|
||||||
});
|
|
||||||
journal.debit(costEntry);
|
|
||||||
inventoryTotal += cost;
|
|
||||||
}
|
|
||||||
// XXX Credit - Income account.
|
|
||||||
const incomeEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: income,
|
|
||||||
account: entry.item.sellAccountId,
|
|
||||||
note: entry.description,
|
|
||||||
});
|
|
||||||
journal.credit(incomeEntry);
|
|
||||||
|
|
||||||
if (inventoryTotal > 0) {
|
|
||||||
// XXX Credit - Inventory account.
|
|
||||||
const inventoryEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: inventoryTotal,
|
|
||||||
account: entry.item.inventoryAccountId,
|
|
||||||
});
|
|
||||||
journal.credit(inventoryEntry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
@@ -142,4 +84,66 @@ export default class SaleInvoicesCost {
|
|||||||
journal.saveBalance(),
|
journal.saveBalance(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes journal entries for given sale invoice.
|
||||||
|
* @param {ISaleInvoice} saleInvoice
|
||||||
|
* @param {JournalPoster} journal
|
||||||
|
*/
|
||||||
|
static saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) {
|
||||||
|
let inventoryTotal: number = 0;
|
||||||
|
const receivableAccount = { id: 10 };
|
||||||
|
const commonEntry = {
|
||||||
|
referenceType: 'SaleInvoice',
|
||||||
|
referenceId: saleInvoice.id,
|
||||||
|
date: saleInvoice.invoiceDate,
|
||||||
|
};
|
||||||
|
const costTransactions: Map<number, number> = new Map(
|
||||||
|
saleInvoice?.costTransactions?.map((trans: IItemEntry) => [
|
||||||
|
trans.entryId, trans.cost,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
// XXX Debit - Receivable account.
|
||||||
|
const receivableEntry = new JournalEntry({
|
||||||
|
...commonEntry,
|
||||||
|
debit: saleInvoice.balance,
|
||||||
|
account: receivableAccount.id,
|
||||||
|
});
|
||||||
|
journal.debit(receivableEntry);
|
||||||
|
|
||||||
|
saleInvoice.entries.forEach((entry: IItemEntry) => {
|
||||||
|
const cost: number = costTransactions.get(entry.id);
|
||||||
|
const income: number = entry.quantity * entry.rate;
|
||||||
|
|
||||||
|
if (entry.item.type === 'inventory' && cost) {
|
||||||
|
// XXX Debit - Cost account.
|
||||||
|
const costEntry = new JournalEntry({
|
||||||
|
...commonEntry,
|
||||||
|
debit: cost,
|
||||||
|
account: entry.item.costAccountId,
|
||||||
|
note: entry.description,
|
||||||
|
});
|
||||||
|
journal.debit(costEntry);
|
||||||
|
inventoryTotal += cost;
|
||||||
|
}
|
||||||
|
// XXX Credit - Income account.
|
||||||
|
const incomeEntry = new JournalEntry({
|
||||||
|
...commonEntry,
|
||||||
|
credit: income,
|
||||||
|
account: entry.item.sellAccountId,
|
||||||
|
note: entry.description,
|
||||||
|
});
|
||||||
|
journal.credit(incomeEntry);
|
||||||
|
|
||||||
|
if (inventoryTotal > 0) {
|
||||||
|
// XXX Credit - Inventory account.
|
||||||
|
const inventoryEntry = new JournalEntry({
|
||||||
|
...commonEntry,
|
||||||
|
credit: inventoryTotal,
|
||||||
|
account: entry.item.inventoryAccountId,
|
||||||
|
});
|
||||||
|
journal.credit(inventoryEntry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,6 @@ export default class Voucher extends mixin(SystemModel) {
|
|||||||
filterActiveVoucher(query) {
|
filterActiveVoucher(query) {
|
||||||
query.where('disabled', false);
|
query.where('disabled', false);
|
||||||
query.where('used', false);
|
query.where('used', false);
|
||||||
query.where('sent', false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Find voucher by its code or id.
|
// Find voucher by its code or id.
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ function resolve(dir) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: [
|
entry: [
|
||||||
'@babel/plugin-transform-runtime',
|
|
||||||
'@/server.js',
|
'@/server.js',
|
||||||
],
|
],
|
||||||
target: 'node',
|
target: 'node',
|
||||||
@@ -43,11 +42,6 @@ module.exports = {
|
|||||||
// // emitWarning: !config.dev.showEslintErrorsInOverlay
|
// // emitWarning: !config.dev.showEslintErrorsInOverlay
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
{
|
|
||||||
use: 'babel-loader',
|
|
||||||
exclude: /(node_modules)/,
|
|
||||||
test: /\.js$/,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: 'ts-loader',
|
use: 'ts-loader',
|
||||||
|
|||||||
Reference in New Issue
Block a user