mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +00:00
fix(Journals): sync posting datetime with jorunal entries.
This commit is contained in:
@@ -106,7 +106,6 @@ export default class InventoryAdjustmentsController extends BaseController {
|
|||||||
) {
|
) {
|
||||||
const { tenantId, user } = req;
|
const { tenantId, user } = req;
|
||||||
const quickInventoryAdjustment = this.matchedBodyData(req);
|
const quickInventoryAdjustment = this.matchedBodyData(req);
|
||||||
console.log(quickInventoryAdjustment);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inventoryAdjustment = await this.inventoryAdjustmentService.createQuickAdjustment(
|
const inventoryAdjustment = await this.inventoryAdjustmentService.createQuickAdjustment(
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
async getPaymentReceiveEditPage(
|
async getPaymentReceiveEditPage(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
|
|||||||
@@ -435,9 +435,7 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
}
|
}
|
||||||
if (error.errorType === 'SALE_INVOICE_NO_IS_REQUIRED') {
|
if (error.errorType === 'SALE_INVOICE_NO_IS_REQUIRED') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
errors: [
|
errors: [{ type: 'SALE_INVOICE_NO_IS_REQUIRED', code: 1500 }],
|
||||||
{ type: 'SALE_INVOICE_NO_IS_REQUIRED', code: 1500 },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,12 @@ exports.up = function(knex) {
|
|||||||
table.integer('item_id').unsigned().nullable().index();
|
table.integer('item_id').unsigned().nullable().index();
|
||||||
table.string('note');
|
table.string('note');
|
||||||
table.integer('user_id').unsigned().index();
|
table.integer('user_id').unsigned().index();
|
||||||
table.integer('index').unsigned();
|
|
||||||
|
table.integer('index_group').unsigned().index();
|
||||||
|
table.integer('index').unsigned().index();
|
||||||
|
|
||||||
table.date('date').index();
|
table.date('date').index();
|
||||||
table.timestamps();
|
table.datetime('created_at').index();
|
||||||
}).raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
const { knexSnakeCaseMappers } = require("objection");
|
const { knexSnakeCaseMappers } = require('objection');
|
||||||
|
|
||||||
exports.up = function(knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable('payment_receives', (table) => {
|
return knex.schema.createTable('payment_receives', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('customer_id').unsigned().index().references('id').inTable('contacts');
|
table
|
||||||
|
.integer('customer_id')
|
||||||
|
.unsigned()
|
||||||
|
.index()
|
||||||
|
.references('id')
|
||||||
|
.inTable('contacts');
|
||||||
table.date('payment_date').index();
|
table.date('payment_date').index();
|
||||||
table.decimal('amount', 13, 3).defaultTo(0);
|
table.decimal('amount', 13, 3).defaultTo(0);
|
||||||
table.string('currency_code', 3);
|
table.string('currency_code', 3);
|
||||||
table.string('reference_no').index();
|
table.string('reference_no').index();
|
||||||
table.integer('deposit_account_id').unsigned().references('id').inTable('accounts');
|
table
|
||||||
|
.integer('deposit_account_id')
|
||||||
|
.unsigned()
|
||||||
|
.references('id')
|
||||||
|
.inTable('accounts');
|
||||||
table.string('payment_receive_no').nullable();
|
table.string('payment_receive_no').nullable();
|
||||||
table.text('statement');
|
table.text('statement');
|
||||||
table.integer('user_id').unsigned().index();
|
table.integer('user_id').unsigned().index();
|
||||||
@@ -16,6 +25,6 @@ exports.up = function(knex) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.down = function(knex) {
|
exports.down = function (knex) {
|
||||||
return knex.schema.dropTableIfExists('payment_receives');
|
return knex.schema.dropTableIfExists('payment_receives');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
|
exports.up = function (knex) {
|
||||||
exports.up = function(knex) {
|
return knex.schema.createTable('inventory_transactions', (table) => {
|
||||||
return knex.schema.createTable('inventory_transactions', table => {
|
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
table.date('date').index();
|
table.date('date').index();
|
||||||
|
|
||||||
table.string('direction').index();
|
table.string('direction').index();
|
||||||
|
table
|
||||||
table.integer('item_id').unsigned().index().references('id').inTable('items');
|
.integer('item_id')
|
||||||
|
.unsigned()
|
||||||
|
.index()
|
||||||
|
.references('id')
|
||||||
|
.inTable('items');
|
||||||
table.integer('quantity').unsigned();
|
table.integer('quantity').unsigned();
|
||||||
table.decimal('rate', 13, 3).unsigned();
|
table.decimal('rate', 13, 3).unsigned();
|
||||||
|
|
||||||
table.integer('lot_number').index();
|
|
||||||
table.integer('cost_account_id').unsigned().index().references('id').inTable('accounts');
|
|
||||||
|
|
||||||
table.string('transaction_type').index();
|
table.string('transaction_type').index();
|
||||||
table.integer('transaction_id').unsigned().index();
|
table.integer('transaction_id').unsigned().index();
|
||||||
|
|
||||||
@@ -21,6 +20,4 @@ exports.up = function(knex) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.down = function(knex) {
|
exports.down = function (knex) {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ exports.up = function(knex) {
|
|||||||
table.integer('discount').unsigned();
|
table.integer('discount').unsigned();
|
||||||
table.integer('quantity').unsigned();
|
table.integer('quantity').unsigned();
|
||||||
table.integer('rate').unsigned();
|
table.integer('rate').unsigned();
|
||||||
|
|
||||||
|
table.integer('sell_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
table.integer('cost_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
|
exports.up = function (knex) {
|
||||||
exports.up = function(knex) {
|
return knex.schema.createTable('inventory_cost_lot_tracker', (table) => {
|
||||||
return knex.schema.createTable('inventory_cost_lot_tracker', table => {
|
|
||||||
table.increments();
|
table.increments();
|
||||||
table.date('date').index();
|
table.date('date').index();
|
||||||
table.string('direction').index();
|
table.string('direction').index();
|
||||||
@@ -10,14 +9,15 @@ exports.up = function(knex) {
|
|||||||
table.decimal('rate', 13, 3);
|
table.decimal('rate', 13, 3);
|
||||||
table.integer('remaining');
|
table.integer('remaining');
|
||||||
table.decimal('cost', 13, 3);
|
table.decimal('cost', 13, 3);
|
||||||
table.integer('lot_number').index();
|
|
||||||
|
|
||||||
table.string('transaction_type').index();
|
table.string('transaction_type').index();
|
||||||
table.integer('transaction_id').unsigned().index();
|
table.integer('transaction_id').unsigned().index();
|
||||||
|
|
||||||
table.integer('entry_id').unsigned().index();
|
table.integer('entry_id').unsigned().index();
|
||||||
});
|
table.datetime('created_at').index();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.down = function(knex) {
|
exports.down = function (knex) {
|
||||||
return knex.schema.dropTableIfExists('inventory_cost_lot_tracker');
|
return knex.schema.dropTableIfExists('inventory_cost_lot_tracker');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,11 +46,13 @@ export interface IBill {
|
|||||||
dueAmount: number,
|
dueAmount: number,
|
||||||
overdueDays: number,
|
overdueDays: number,
|
||||||
|
|
||||||
invLotNumber: string,
|
|
||||||
openedAt: Date | string,
|
openedAt: Date | string,
|
||||||
|
|
||||||
entries: IItemEntry[],
|
entries: IItemEntry[],
|
||||||
userId: number,
|
userId: number,
|
||||||
|
|
||||||
|
createdAt: Date,
|
||||||
|
updateAt: Date,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IBillsFilter extends IDynamicListFilterDTO {
|
export interface IBillsFilter extends IDynamicListFilterDTO {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { LongDateFormatKey } from "moment";
|
||||||
|
|
||||||
|
|
||||||
export interface IBillPaymentEntry {
|
export interface IBillPaymentEntry {
|
||||||
@@ -18,6 +19,8 @@ export interface IBillPayment {
|
|||||||
userId: number,
|
userId: number,
|
||||||
entries: IBillPaymentEntry[],
|
entries: IBillPaymentEntry[],
|
||||||
statement: string,
|
statement: string,
|
||||||
|
createdAt: Date,
|
||||||
|
updatedAt: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBillPaymentEntryDTO {
|
export interface IBillPaymentEntryDTO {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface IInventoryAdjustment {
|
|||||||
entries: IInventoryAdjustmentEntry[];
|
entries: IInventoryAdjustmentEntry[];
|
||||||
userId: number;
|
userId: number;
|
||||||
publishedAt?: Date|null;
|
publishedAt?: Date|null;
|
||||||
|
createdAt?: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IInventoryAdjustmentEntry {
|
export interface IInventoryAdjustmentEntry {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export interface IInventoryTransaction {
|
|||||||
rate: number,
|
rate: number,
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: number,
|
transactionId: number,
|
||||||
lotNumber: number,
|
|
||||||
entryId: number,
|
entryId: number,
|
||||||
createdAt?: Date,
|
createdAt?: Date,
|
||||||
updatedAt?: Date,
|
updatedAt?: Date,
|
||||||
@@ -25,10 +24,12 @@ export interface IInventoryLotCost {
|
|||||||
rate: number,
|
rate: number,
|
||||||
remaining: number,
|
remaining: number,
|
||||||
cost: number,
|
cost: number,
|
||||||
lotNumber: number,
|
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: number,
|
transactionId: number,
|
||||||
entryId: number
|
costAccountId: number,
|
||||||
|
sellAccountId: number,
|
||||||
|
entryId: number,
|
||||||
|
createdAt: Date,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IItemsQuantityChanges {
|
export interface IItemsQuantityChanges {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ export interface IItemEntry {
|
|||||||
discount: number,
|
discount: number,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
rate: number,
|
rate: number,
|
||||||
|
|
||||||
|
sellAccountId: number,
|
||||||
|
costAccountId: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IItemEntryDTO {
|
export interface IItemEntryDTO {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export interface IManualJournal {
|
|||||||
description: string;
|
description: string;
|
||||||
userId: number;
|
userId: number;
|
||||||
entries: IManualJournalEntry[];
|
entries: IManualJournalEntry[];
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IManualJournalEntry {
|
export interface IManualJournalEntry {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export interface IPaymentReceive {
|
|||||||
statement: string;
|
statement: string;
|
||||||
entries: IPaymentReceiveEntry[];
|
entries: IPaymentReceiveEntry[];
|
||||||
userId: number;
|
userId: number;
|
||||||
|
createdAt: Date,
|
||||||
|
updatedAt: Date,
|
||||||
}
|
}
|
||||||
export interface IPaymentReceiveCreateDTO {
|
export interface IPaymentReceiveCreateDTO {
|
||||||
customerId: number;
|
customerId: number;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface ISaleInvoice {
|
|||||||
entries: IItemEntry[];
|
entries: IItemEntry[];
|
||||||
deliveredAt: string | Date;
|
deliveredAt: string | Date;
|
||||||
userId: number;
|
userId: number;
|
||||||
|
createdAt: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISaleInvoiceDTO {
|
export interface ISaleInvoiceDTO {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export interface ISaleReceipt {
|
|||||||
statement: string;
|
statement: string;
|
||||||
closedAt: Date | string;
|
closedAt: Date | string;
|
||||||
entries: any[];
|
entries: any[];
|
||||||
|
createdAt: Date,
|
||||||
|
updatedAt: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISalesReceiptsFilter {}
|
export interface ISalesReceiptsFilter {}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export default class WriteInvoicesJournalEntries {
|
|||||||
agenda.on(`complete:${eventName}`, this.onJobCompleted.bind(this));
|
agenda.on(`complete:${eventName}`, this.onJobCompleted.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the job execuation.
|
||||||
|
*/
|
||||||
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, tenantId } = job.attrs.data;
|
const { startingDate, tenantId } = job.attrs.data;
|
||||||
|
|||||||
@@ -112,12 +112,10 @@ export default class AccountTransaction extends TenantModel {
|
|||||||
filterContactIds(query, contactIds) {
|
filterContactIds(query, contactIds) {
|
||||||
query.whereIn('contact_id', contactIds);
|
query.whereIn('contact_id', contactIds);
|
||||||
},
|
},
|
||||||
|
|
||||||
openingBalance(query, fromDate) {
|
openingBalance(query, fromDate) {
|
||||||
query.modify('filterDateRange', null, fromDate)
|
query.modify('filterDateRange', null, fromDate)
|
||||||
query.modify('sumationCreditDebit')
|
query.modify('sumationCreditDebit')
|
||||||
},
|
},
|
||||||
|
|
||||||
closingBalance(query, toDate) {
|
closingBalance(query, toDate) {
|
||||||
query.modify('filterDateRange', null, toDate)
|
query.modify('filterDateRange', null, toDate)
|
||||||
query.modify('sumationCreditDebit')
|
query.modify('sumationCreditDebit')
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ export default (Model) => {
|
|||||||
const maybePromise = super.$beforeUpdate(opt, context);
|
const maybePromise = super.$beforeUpdate(opt, context);
|
||||||
|
|
||||||
return Promise.resolve(maybePromise).then(() => {
|
return Promise.resolve(maybePromise).then(() => {
|
||||||
if (this.timestamps[1]) {
|
const key = this.timestamps[1];
|
||||||
this[this.timestamps[1]] = moment().format('YYYY/MM/DD HH:mm:ss');
|
|
||||||
|
if (key && !this[key]) {
|
||||||
|
this[key] = moment().format('YYYY/MM/DD HH:mm:ss');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -21,8 +23,10 @@ export default (Model) => {
|
|||||||
const maybePromise = super.$beforeInsert(context);
|
const maybePromise = super.$beforeInsert(context);
|
||||||
|
|
||||||
return Promise.resolve(maybePromise).then(() => {
|
return Promise.resolve(maybePromise).then(() => {
|
||||||
if (this.timestamps[0]) {
|
const key = this.timestamps[0];
|
||||||
this[this.timestamps[0]] = moment().format('YYYY/MM/DD HH:mm:ss');
|
|
||||||
|
if (key && !this[key]) {
|
||||||
|
this[key] = moment().format('YYYY/MM/DD HH:mm:ss');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default class InventoryCostLotTracker extends TenantModel {
|
|||||||
*/
|
*/
|
||||||
static get modifiers() {
|
static get modifiers() {
|
||||||
return {
|
return {
|
||||||
groupedEntriesCost(query) {
|
groupedEntriesCost(query) {
|
||||||
query.select(['date', 'item_id', 'transaction_id', 'transaction_type']);
|
query.select(['date', 'item_id', 'transaction_id', 'transaction_type']);
|
||||||
query.sum('cost as cost');
|
query.sum('cost as cost');
|
||||||
|
|
||||||
@@ -46,12 +46,13 @@ export default class InventoryCostLotTracker extends TenantModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Item = require('models/Item');
|
const Item = require('models/Item');
|
||||||
|
const SaleInvoice = require('models/SaleInvoice');
|
||||||
|
const ItemEntry = require('models/ItemEntry');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: {
|
item: {
|
||||||
@@ -62,6 +63,25 @@ export default class InventoryCostLotTracker extends TenantModel {
|
|||||||
to: 'items.id',
|
to: 'items.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
invoice: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: SaleInvoice.default,
|
||||||
|
join: {
|
||||||
|
from: 'inventory_cost_lot_tracker.transactionId',
|
||||||
|
to: 'sales_invoices.id',
|
||||||
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('transaction_type', 'SaleInvoice');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
itemEntry: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: ItemEntry.default,
|
||||||
|
join: {
|
||||||
|
from: 'inventory_cost_lot_tracker.entryId',
|
||||||
|
to: 'items_entries.id',
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export default class InventoryTransaction extends TenantModel {
|
|||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Item = require('models/Item');
|
const Item = require('models/Item');
|
||||||
|
const ItemEntry = require('models/ItemEntry');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: {
|
item: {
|
||||||
@@ -52,6 +53,14 @@ export default class InventoryTransaction extends TenantModel {
|
|||||||
to: 'items.id',
|
to: 'items.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
itemEntry: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: ItemEntry.default,
|
||||||
|
join: {
|
||||||
|
from: 'inventory_transactions.entryId',
|
||||||
|
to: 'items_entries.id',
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ interface IJournalTransactionsFilter {
|
|||||||
contactType?: string,
|
contactType?: string,
|
||||||
referenceType?: string[],
|
referenceType?: string[],
|
||||||
referenceId?: number[],
|
referenceId?: number[],
|
||||||
index: number|number[]
|
index: number|number[],
|
||||||
|
indexGroup: number|number[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class AccountTransactionsRepository extends TenantRepository {
|
export default class AccountTransactionsRepository extends TenantRepository {
|
||||||
@@ -58,6 +59,13 @@ export default class AccountTransactionsRepository extends TenantRepository {
|
|||||||
query.where('index', filter.index);
|
query.where('index', filter.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (filter.indexGroup) {
|
||||||
|
if (Array.isArray(filter.indexGroup)) {
|
||||||
|
query.whereIn('index_group', filter.indexGroup);
|
||||||
|
} else {
|
||||||
|
query.where('index_group', filter.indexGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { sumBy, chain } from 'lodash';
|
import moment from 'moment';
|
||||||
import moment, { LongDateFormatKey } from 'moment';
|
import {
|
||||||
import { IBill, IManualJournalEntry, ISaleReceipt, ISystemUser } from 'interfaces';
|
IBill,
|
||||||
|
IManualJournalEntry,
|
||||||
|
ISaleReceipt,
|
||||||
|
ISystemUser,
|
||||||
|
} from 'interfaces';
|
||||||
import JournalPoster from './JournalPoster';
|
import JournalPoster from './JournalPoster';
|
||||||
import JournalEntry from './JournalEntry';
|
import JournalEntry from './JournalEntry';
|
||||||
import { AccountTransaction } from 'models';
|
|
||||||
import {
|
import {
|
||||||
IInventoryTransaction,
|
|
||||||
IManualJournal,
|
IManualJournal,
|
||||||
IExpense,
|
IExpense,
|
||||||
IExpenseCategory,
|
IExpenseCategory,
|
||||||
@@ -14,6 +16,7 @@ import {
|
|||||||
IInventoryLotCost,
|
IInventoryLotCost,
|
||||||
IItemEntry,
|
IItemEntry,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
|
import { increment } from 'utils';
|
||||||
|
|
||||||
export default class JournalCommands {
|
export default class JournalCommands {
|
||||||
journal: JournalPoster;
|
journal: JournalPoster;
|
||||||
@@ -61,6 +64,8 @@ export default class JournalCommands {
|
|||||||
|
|
||||||
referenceNumber: bill.referenceNo,
|
referenceNumber: bill.referenceNo,
|
||||||
transactionNumber: bill.billNumber,
|
transactionNumber: bill.billNumber,
|
||||||
|
|
||||||
|
createdAt: bill.createdAt,
|
||||||
};
|
};
|
||||||
// Overrides the old bill entries.
|
// Overrides the old bill entries.
|
||||||
if (override) {
|
if (override) {
|
||||||
@@ -90,9 +95,9 @@ export default class JournalCommands {
|
|||||||
account:
|
account:
|
||||||
['inventory'].indexOf(item.type) !== -1
|
['inventory'].indexOf(item.type) !== -1
|
||||||
? item.inventoryAccountId
|
? item.inventoryAccountId
|
||||||
: item.costAccountId,
|
: entry.costAccountId,
|
||||||
index: index + 2,
|
index: index + 2,
|
||||||
itemId: entry.itemId
|
itemId: entry.itemId,
|
||||||
});
|
});
|
||||||
this.journal.debit(debitEntry);
|
this.journal.debit(debitEntry);
|
||||||
});
|
});
|
||||||
@@ -257,7 +262,7 @@ export default class JournalCommands {
|
|||||||
const transactions = await transactionsRepository.journal({
|
const transactions = await transactionsRepository.journal({
|
||||||
fromDate: startingDate,
|
fromDate: startingDate,
|
||||||
referenceType: ['SaleInvoice'],
|
referenceType: ['SaleInvoice'],
|
||||||
index: [3, 4],
|
indexGroup: 20
|
||||||
});
|
});
|
||||||
this.journal.fromTransactions(transactions);
|
this.journal.fromTransactions(transactions);
|
||||||
this.journal.removeEntries();
|
this.journal.removeEntries();
|
||||||
@@ -265,11 +270,9 @@ export default class JournalCommands {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts sale invoice the income journal entries.
|
* Reverts sale invoice the income journal entries.
|
||||||
* @param {number} saleInvoiceId
|
* @param {number} saleInvoiceId
|
||||||
*/
|
*/
|
||||||
async revertInvoiceIncomeEntries(
|
async revertInvoiceIncomeEntries(saleInvoiceId: number) {
|
||||||
saleInvoiceId: number,
|
|
||||||
) {
|
|
||||||
const { transactionsRepository } = this.repositories;
|
const { transactionsRepository } = this.repositories;
|
||||||
|
|
||||||
const transactions = await transactionsRepository.journal({
|
const transactions = await transactionsRepository.journal({
|
||||||
@@ -289,6 +292,7 @@ export default class JournalCommands {
|
|||||||
const commonEntry = {
|
const commonEntry = {
|
||||||
transaction_number: manualJournalObj.journalNumber,
|
transaction_number: manualJournalObj.journalNumber,
|
||||||
reference_number: manualJournalObj.reference,
|
reference_number: manualJournalObj.reference,
|
||||||
|
createdAt: manualJournalObj.createdAt,
|
||||||
};
|
};
|
||||||
manualJournalObj.entries.forEach((entry: IManualJournalEntry) => {
|
manualJournalObj.entries.forEach((entry: IManualJournalEntry) => {
|
||||||
const jouranlEntry = new JournalEntry({
|
const jouranlEntry = new JournalEntry({
|
||||||
@@ -317,36 +321,50 @@ export default class JournalCommands {
|
|||||||
* -------
|
* -------
|
||||||
* - Cost of goods sold -> Debit -> YYYY
|
* - Cost of goods sold -> Debit -> YYYY
|
||||||
* - Inventory assets -> Credit -> YYYY
|
* - Inventory assets -> Credit -> YYYY
|
||||||
*
|
* --------
|
||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @param {JournalPoster} journal
|
* @param {JournalPoster} journal
|
||||||
*/
|
*/
|
||||||
saleInvoiceInventoryCost(
|
saleInvoiceInventoryCost(
|
||||||
inventoryCostLot: IInventoryLotCost & { item: IItem }
|
inventoryCostLots: IInventoryLotCost &
|
||||||
|
{ item: IItem; itemEntry: IItemEntry }[]
|
||||||
) {
|
) {
|
||||||
const commonEntry = {
|
const getIndexIncrement = increment(0);
|
||||||
referenceType: inventoryCostLot.transactionType,
|
|
||||||
referenceId: inventoryCostLot.transactionId,
|
inventoryCostLots.forEach(
|
||||||
date: inventoryCostLot.date,
|
(
|
||||||
};
|
inventoryCostLot: IInventoryLotCost & {
|
||||||
// XXX Debit - Cost account.
|
item: IItem;
|
||||||
const costEntry = new JournalEntry({
|
itemEntry: IItemEntry;
|
||||||
...commonEntry,
|
}
|
||||||
debit: inventoryCostLot.cost,
|
) => {
|
||||||
account: inventoryCostLot.item.costAccountId,
|
const commonEntry = {
|
||||||
index: 3,
|
referenceType: inventoryCostLot.transactionType,
|
||||||
itemId: inventoryCostLot.itemId
|
referenceId: inventoryCostLot.transactionId,
|
||||||
});
|
date: inventoryCostLot.date,
|
||||||
// XXX Credit - Inventory account.
|
indexGroup: 20,
|
||||||
const inventoryEntry = new JournalEntry({
|
createdAt: inventoryCostLot.createdAt,
|
||||||
...commonEntry,
|
};
|
||||||
credit: inventoryCostLot.cost,
|
// XXX Debit - Cost account.
|
||||||
account: inventoryCostLot.item.inventoryAccountId,
|
const costEntry = new JournalEntry({
|
||||||
index: 4,
|
...commonEntry,
|
||||||
itemId: inventoryCostLot.itemId
|
debit: inventoryCostLot.cost,
|
||||||
});
|
account: inventoryCostLot.itemEntry.costAccountId,
|
||||||
this.journal.credit(inventoryEntry);
|
itemId: inventoryCostLot.itemId,
|
||||||
this.journal.debit(costEntry);
|
index: getIndexIncrement(),
|
||||||
|
});
|
||||||
|
// XXX Credit - Inventory account.
|
||||||
|
const inventoryEntry = new JournalEntry({
|
||||||
|
...commonEntry,
|
||||||
|
credit: inventoryCostLot.cost,
|
||||||
|
account: inventoryCostLot.item.inventoryAccountId,
|
||||||
|
itemId: inventoryCostLot.itemId,
|
||||||
|
index: getIndexIncrement(),
|
||||||
|
});
|
||||||
|
this.journal.credit(inventoryEntry);
|
||||||
|
this.journal.debit(costEntry);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,7 +372,7 @@ export default class JournalCommands {
|
|||||||
* -----
|
* -----
|
||||||
* - Receivable accounts -> Debit -> XXXX
|
* - Receivable accounts -> Debit -> XXXX
|
||||||
* - Income -> Credit -> XXXX
|
* - Income -> Credit -> XXXX
|
||||||
*
|
*
|
||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @param {number} receivableAccountsId
|
* @param {number} receivableAccountsId
|
||||||
* @param {number} authorizedUserId
|
* @param {number} authorizedUserId
|
||||||
@@ -373,6 +391,9 @@ export default class JournalCommands {
|
|||||||
|
|
||||||
transactionNumber: saleInvoice.invoiceNo,
|
transactionNumber: saleInvoice.invoiceNo,
|
||||||
referenceNumber: saleInvoice.referenceNo,
|
referenceNumber: saleInvoice.referenceNo,
|
||||||
|
|
||||||
|
createdAt: saleInvoice.createdAt,
|
||||||
|
indexGroup: 10,
|
||||||
};
|
};
|
||||||
// XXX Debit - Receivable account.
|
// XXX Debit - Receivable account.
|
||||||
const receivableEntry = new JournalEntry({
|
const receivableEntry = new JournalEntry({
|
||||||
@@ -384,22 +405,20 @@ export default class JournalCommands {
|
|||||||
});
|
});
|
||||||
this.journal.debit(receivableEntry);
|
this.journal.debit(receivableEntry);
|
||||||
|
|
||||||
saleInvoice.entries.forEach(
|
saleInvoice.entries.forEach((entry: IItemEntry, index: number) => {
|
||||||
(entry: IItemEntry & { item: IItem }, index: number) => {
|
const income: number = entry.quantity * entry.rate;
|
||||||
const income: number = entry.quantity * entry.rate;
|
|
||||||
|
|
||||||
// XXX Credit - Income account.
|
// XXX Credit - Income account.
|
||||||
const incomeEntry = new JournalEntry({
|
const incomeEntry = new JournalEntry({
|
||||||
...commonEntry,
|
...commonEntry,
|
||||||
credit: income,
|
credit: income,
|
||||||
account: entry.item.sellAccountId,
|
account: entry.sellAccountId,
|
||||||
note: entry.description,
|
note: entry.description,
|
||||||
index: index + 2,
|
index: index + 2,
|
||||||
itemId: entry.itemId,
|
itemId: entry.itemId,
|
||||||
});
|
});
|
||||||
this.journal.credit(incomeEntry);
|
this.journal.credit(incomeEntry);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,7 +426,7 @@ export default class JournalCommands {
|
|||||||
* -----
|
* -----
|
||||||
* - Deposit account -> Debit -> XXXX
|
* - Deposit account -> Debit -> XXXX
|
||||||
* - Income -> Credit -> XXXX
|
* - Income -> Credit -> XXXX
|
||||||
*
|
*
|
||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @param {number} receivableAccountsId
|
* @param {number} receivableAccountsId
|
||||||
* @param {number} authorizedUserId
|
* @param {number} authorizedUserId
|
||||||
@@ -415,7 +434,7 @@ export default class JournalCommands {
|
|||||||
async saleReceiptIncomeEntries(
|
async saleReceiptIncomeEntries(
|
||||||
saleReceipt: ISaleReceipt & {
|
saleReceipt: ISaleReceipt & {
|
||||||
entries: IItemEntry & { item: IItem };
|
entries: IItemEntry & { item: IItem };
|
||||||
},
|
}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const commonEntry = {
|
const commonEntry = {
|
||||||
referenceType: 'SaleReceipt',
|
referenceType: 'SaleReceipt',
|
||||||
@@ -424,6 +443,7 @@ export default class JournalCommands {
|
|||||||
userId: saleReceipt.userId,
|
userId: saleReceipt.userId,
|
||||||
transactionNumber: saleReceipt.receiptNumber,
|
transactionNumber: saleReceipt.receiptNumber,
|
||||||
referenceNumber: saleReceipt.referenceNo,
|
referenceNumber: saleReceipt.referenceNo,
|
||||||
|
createdAt: saleReceipt.createdAt,
|
||||||
};
|
};
|
||||||
// XXX Debit - Deposit account.
|
// XXX Debit - Deposit account.
|
||||||
const depositEntry = new JournalEntry({
|
const depositEntry = new JournalEntry({
|
||||||
|
|||||||
@@ -164,10 +164,12 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const balanceEntries = chain(balanceChanges)
|
const balanceEntries = chain(balanceChanges)
|
||||||
.map((change) => change.entries.map(entry => ({
|
.map((change) =>
|
||||||
...entry,
|
change.entries.map((entry) => ({
|
||||||
contactId: change.contactId
|
...entry,
|
||||||
})))
|
contactId: change.contactId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
.flatten()
|
.flatten()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
@@ -376,6 +378,8 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
const { transactionsRepository } = this.repositories;
|
const { transactionsRepository } = this.repositories;
|
||||||
const saveOperations: Promise<void>[] = [];
|
const saveOperations: Promise<void>[] = [];
|
||||||
|
|
||||||
|
this.logger.info('[journal] trying to insert accounts transactions.');
|
||||||
|
|
||||||
this.entries.forEach((entry) => {
|
this.entries.forEach((entry) => {
|
||||||
const oper = transactionsRepository.create({
|
const oper = transactionsRepository.create({
|
||||||
accountId: entry.account,
|
accountId: entry.account,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker'
|
|||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
import SettingsMiddleware from 'api/middleware/SettingsMiddleware';
|
|
||||||
|
|
||||||
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
|
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
|
||||||
|
|
||||||
@@ -35,20 +34,27 @@ export default class InventoryService {
|
|||||||
/**
|
/**
|
||||||
* Transforms the items entries to inventory transactions.
|
* Transforms the items entries to inventory transactions.
|
||||||
*/
|
*/
|
||||||
transformItemEntriesToInventory(
|
transformItemEntriesToInventory(transaction: {
|
||||||
itemEntries: IItemEntry[],
|
transactionId: number;
|
||||||
direction: TInventoryTransactionDirection,
|
transactionType: IItemEntryTransactionType;
|
||||||
date: Date | string,
|
|
||||||
lotNumber: number
|
date: Date | string;
|
||||||
): IInventoryTransaction[] {
|
direction: TInventoryTransactionDirection;
|
||||||
return itemEntries.map((entry: IItemEntry) => ({
|
entries: IItemEntry[];
|
||||||
...pick(entry, ['itemId', 'quantity', 'rate']),
|
createdAt: Date;
|
||||||
lotNumber,
|
}): IInventoryTransaction[] {
|
||||||
transactionType: entry.referenceType,
|
return transaction.entries.map((entry: IItemEntry) => ({
|
||||||
transactionId: entry.referenceId,
|
...pick(entry, [
|
||||||
direction,
|
'itemId',
|
||||||
date,
|
'quantity',
|
||||||
|
'rate',
|
||||||
|
]),
|
||||||
|
transactionType: transaction.transactionType,
|
||||||
|
transactionId: transaction.transactionId,
|
||||||
|
direction: transaction.direction,
|
||||||
|
date: transaction.date,
|
||||||
entryId: entry.id,
|
entryId: entry.id,
|
||||||
|
createdAt: transaction.createdAt,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +206,6 @@ export default class InventoryService {
|
|||||||
}
|
}
|
||||||
return InventoryTransaction.query().insert({
|
return InventoryTransaction.query().insert({
|
||||||
...inventoryEntry,
|
...inventoryEntry,
|
||||||
lotNumber: inventoryEntry.lotNumber,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,31 +220,24 @@ export default class InventoryService {
|
|||||||
*/
|
*/
|
||||||
async recordInventoryTransactionsFromItemsEntries(
|
async recordInventoryTransactionsFromItemsEntries(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
transactionId: number,
|
transaction: {
|
||||||
transactionType: IItemEntryTransactionType,
|
transactionId: number;
|
||||||
transactionDate: Date | string,
|
transactionType: IItemEntryTransactionType;
|
||||||
transactionDirection: TInventoryTransactionDirection,
|
|
||||||
|
date: Date | string;
|
||||||
|
direction: TInventoryTransactionDirection;
|
||||||
|
entries: IItemEntry[];
|
||||||
|
createdAt: Date | string;
|
||||||
|
},
|
||||||
override: boolean = false
|
override: boolean = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Retrieve the next inventory lot number.
|
|
||||||
const lotNumber = this.getNextLotNumber(tenantId);
|
|
||||||
|
|
||||||
// Loads the inventory items entries of the given sale invoice.
|
|
||||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
|
||||||
tenantId,
|
|
||||||
transactionType,
|
|
||||||
transactionId
|
|
||||||
);
|
|
||||||
// Can't continue if there is no entries has inventory items in the invoice.
|
// Can't continue if there is no entries has inventory items in the invoice.
|
||||||
if (inventoryEntries.length <= 0) {
|
if (transaction.entries.length <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Inventory transactions.
|
// Inventory transactions.
|
||||||
const inventoryTranscations = this.transformItemEntriesToInventory(
|
const inventoryTranscations = this.transformItemEntriesToInventory(
|
||||||
inventoryEntries,
|
transaction
|
||||||
transactionDirection,
|
|
||||||
transactionDate,
|
|
||||||
lotNumber
|
|
||||||
);
|
);
|
||||||
// Records the inventory transactions of the given sale invoice.
|
// Records the inventory transactions of the given sale invoice.
|
||||||
await this.recordInventoryTransactions(
|
await this.recordInventoryTransactions(
|
||||||
@@ -247,8 +245,6 @@ export default class InventoryService {
|
|||||||
inventoryTranscations,
|
inventoryTranscations,
|
||||||
override
|
override
|
||||||
);
|
);
|
||||||
// Increment and save the next lot number settings.
|
|
||||||
await this.incrementNextLotNumber(tenantId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -309,49 +305,10 @@ export default class InventoryService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the lot number after the increment.
|
|
||||||
* @param {number} tenantId - Tenant id.
|
|
||||||
*/
|
|
||||||
getNextLotNumber(tenantId: number) {
|
|
||||||
const settings = this.tenancy.settings(tenantId);
|
|
||||||
|
|
||||||
const LOT_NUMBER_KEY = 'lot_number_increment';
|
|
||||||
const storedLotNumber = settings.find({ key: LOT_NUMBER_KEY });
|
|
||||||
|
|
||||||
return storedLotNumber && storedLotNumber.value
|
|
||||||
? parseInt(storedLotNumber.value, 10)
|
|
||||||
: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment the next inventory LOT number.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @return {Promise<number>}
|
|
||||||
*/
|
|
||||||
async incrementNextLotNumber(tenantId: number) {
|
|
||||||
const settings = this.tenancy.settings(tenantId);
|
|
||||||
|
|
||||||
const LOT_NUMBER_KEY = 'lot_number_increment';
|
|
||||||
const storedLotNumber = settings.find({ key: LOT_NUMBER_KEY });
|
|
||||||
|
|
||||||
let lotNumber = 1;
|
|
||||||
|
|
||||||
if (storedLotNumber && storedLotNumber.value) {
|
|
||||||
lotNumber = parseInt(storedLotNumber.value, 10);
|
|
||||||
lotNumber += 1;
|
|
||||||
}
|
|
||||||
settings.set({ key: LOT_NUMBER_KEY }, lotNumber);
|
|
||||||
|
|
||||||
await settings.save();
|
|
||||||
|
|
||||||
return lotNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark item cost computing is running.
|
* Mark item cost computing is running.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {boolean} isRunning -
|
* @param {boolean} isRunning -
|
||||||
*/
|
*/
|
||||||
async markItemsCostComputeRunning(
|
async markItemsCostComputeRunning(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
@@ -368,16 +325,16 @@ export default class InventoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
isItemsCostComputeRunning(tenantId) {
|
isItemsCostComputeRunning(tenantId) {
|
||||||
const settings = this.tenancy.settings(tenantId);
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
return settings.get({
|
return settings.get({
|
||||||
key: 'cost_compute_running',
|
key: 'cost_compute_running',
|
||||||
group: 'inventory'
|
group: 'inventory',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import ItemsService from 'services/Items/ItemsService';
|
|||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
import HasTenancyService from 'services/Tenancy/TenancyService';
|
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||||
import InventoryService from './Inventory';
|
import InventoryService from './Inventory';
|
||||||
|
import { increment } from 'utils';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
INVENTORY_ADJUSTMENT_NOT_FOUND: 'INVENTORY_ADJUSTMENT_NOT_FOUND',
|
INVENTORY_ADJUSTMENT_NOT_FOUND: 'INVENTORY_ADJUSTMENT_NOT_FOUND',
|
||||||
@@ -310,24 +311,21 @@ export default class InventoryAdjustmentService {
|
|||||||
inventoryAdjustment: IInventoryAdjustment,
|
inventoryAdjustment: IInventoryAdjustment,
|
||||||
override: boolean = false
|
override: boolean = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Gets the next inventory lot number.
|
|
||||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
|
||||||
|
|
||||||
const commonTransaction = {
|
const commonTransaction = {
|
||||||
direction: inventoryAdjustment.inventoryDirection,
|
direction: inventoryAdjustment.inventoryDirection,
|
||||||
date: inventoryAdjustment.date,
|
date: inventoryAdjustment.date,
|
||||||
transactionType: 'InventoryAdjustment',
|
transactionType: 'InventoryAdjustment',
|
||||||
transactionId: inventoryAdjustment.id,
|
transactionId: inventoryAdjustment.id,
|
||||||
|
createdAt: inventoryAdjustment.createdAt
|
||||||
};
|
};
|
||||||
const inventoryTransactions = [];
|
const inventoryTransactions = [];
|
||||||
|
|
||||||
inventoryAdjustment.entries.forEach((entry) => {
|
inventoryAdjustment.entries.forEach((entry) => {
|
||||||
inventoryTransactions.push({
|
inventoryTransactions.push({
|
||||||
...commonTransaction,
|
...commonTransaction,
|
||||||
itemId: entry.itemId,
|
itemId: entry.itemId,
|
||||||
quantity: entry.quantity,
|
quantity: entry.quantity,
|
||||||
rate: entry.cost,
|
rate: entry.cost,
|
||||||
lotNumber,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Saves the given inventory transactions to the storage.
|
// Saves the given inventory transactions to the storage.
|
||||||
@@ -335,9 +333,7 @@ export default class InventoryAdjustmentService {
|
|||||||
tenantId,
|
tenantId,
|
||||||
inventoryTransactions,
|
inventoryTransactions,
|
||||||
override
|
override
|
||||||
);
|
)
|
||||||
// Increment and save the next lot number settings.
|
|
||||||
await this.inventoryService.incrementNextLotNumber(tenantId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ export default class InventoryAverageCostMethod
|
|||||||
.modify('filterDateRange', this.startingDate)
|
.modify('filterDateRange', this.startingDate)
|
||||||
.orderBy('date', 'ASC')
|
.orderBy('date', 'ASC')
|
||||||
.orderByRaw("FIELD(direction, 'IN', 'OUT')")
|
.orderByRaw("FIELD(direction, 'IN', 'OUT')")
|
||||||
.orderBy('lot_number', 'ASC')
|
.orderBy('createdAt', 'ASC')
|
||||||
.where('item_id', this.itemId)
|
.where('item_id', this.itemId)
|
||||||
.withGraphFetched('item');
|
.withGraphFetched('item');
|
||||||
|
|
||||||
// Tracking inventroy transactions and retrieve cost transactions based on
|
// Tracking inventroy transactions and retrieve cost transactions based on
|
||||||
// average rate cost method.
|
// average rate cost method.
|
||||||
const costTransactions = this.trackingCostTransactions(
|
const costTransactions = this.trackingCostTransactions(
|
||||||
@@ -164,7 +164,9 @@ export default class InventoryAverageCostMethod
|
|||||||
'entryId',
|
'entryId',
|
||||||
'transactionId',
|
'transactionId',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'lotNumber',
|
'createdAt',
|
||||||
|
'sellAccountId',
|
||||||
|
'costAccountId',
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
switch (invTransaction.direction) {
|
switch (invTransaction.direction) {
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ export default class InventoryCostMethod {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the inventory lots costs transactions in bulk.
|
* Stores the inventory lots costs transactions in bulk.
|
||||||
* @param {IInventoryLotCost[]} costLotsTransactions
|
* @param {IInventoryLotCost[]} costLotsTransactions
|
||||||
* @return {Promise[]}
|
* @return {Promise[]}
|
||||||
*/
|
*/
|
||||||
public storeInventoryLotsCost(costLotsTransactions: IInventoryLotCost[]): Promise<object> {
|
public storeInventoryLotsCost(
|
||||||
|
costLotsTransactions: IInventoryLotCost[]
|
||||||
|
): Promise<object> {
|
||||||
const { InventoryCostLotTracker } = this.tenantModels;
|
const { InventoryCostLotTracker } = this.tenantModels;
|
||||||
const opers: any = [];
|
const opers: any = [];
|
||||||
|
|
||||||
@@ -32,15 +34,13 @@ export default class InventoryCostMethod {
|
|||||||
.where('id', transaction.lotTransId)
|
.where('id', transaction.lotTransId)
|
||||||
.decrement('remaining', transaction.decrement);
|
.decrement('remaining', transaction.decrement);
|
||||||
opers.push(decrementOper);
|
opers.push(decrementOper);
|
||||||
|
} else if (!transaction.lotTransId) {
|
||||||
} else if(!transaction.lotTransId) {
|
const operation = InventoryCostLotTracker.query().insert({
|
||||||
const operation = InventoryCostLotTracker.query()
|
...omit(transaction, ['decrement', 'invTransId', 'lotTransId']),
|
||||||
.insert({
|
});
|
||||||
...omit(transaction, ['decrement', 'invTransId', 'lotTransId']),
|
|
||||||
});
|
|
||||||
opers.push(operation);
|
opers.push(operation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Promise.all(opers);
|
return Promise.all(opers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,29 @@ export default class ItemsEntriesService {
|
|||||||
return inventoryItemsEntries;
|
return inventoryItemsEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given entries to inventory entries.
|
||||||
|
* @param {IItemEntry[]} entries -
|
||||||
|
* @returns {IItemEntry[]}
|
||||||
|
*/
|
||||||
|
public async filterInventoryEntries(
|
||||||
|
tenantId: number,
|
||||||
|
entries: IItemEntry[]
|
||||||
|
): Promise<IItemEntry[]> {
|
||||||
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
|
const entriesItemsIds = entries.map((e) => e.itemId);
|
||||||
|
|
||||||
|
// Retrieve entries inventory items.
|
||||||
|
const inventoryItems = await Item.query()
|
||||||
|
.whereIn('id', entriesItemsIds)
|
||||||
|
.where('type', 'inventory');
|
||||||
|
|
||||||
|
const inventoryEntries = entries.filter((entry) =>
|
||||||
|
inventoryItems.some((item) => item.id === entry.itemId)
|
||||||
|
);
|
||||||
|
return inventoryEntries;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the entries items ids.
|
* Validates the entries items ids.
|
||||||
* @async
|
* @async
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
ACCOUNT_TYPE,
|
ACCOUNT_TYPE,
|
||||||
} from 'data/AccountTypes';
|
} from 'data/AccountTypes';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
import { AccountTransaction } from 'models';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ItemsService implements IItemsService {
|
export default class ItemsService implements IItemsService {
|
||||||
|
|||||||
@@ -569,7 +569,12 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
|||||||
credit: 0,
|
credit: 0,
|
||||||
referenceId: billPayment.id,
|
referenceId: billPayment.id,
|
||||||
referenceType: 'BillPayment',
|
referenceType: 'BillPayment',
|
||||||
|
|
||||||
|
transactionNumber: billPayment.paymentNumber,
|
||||||
|
referenceNumber: billPayment.reference,
|
||||||
|
|
||||||
date: formattedDate,
|
date: formattedDate,
|
||||||
|
createdAt: billPayment.createdAt,
|
||||||
};
|
};
|
||||||
if (override) {
|
if (override) {
|
||||||
const transactions = await AccountTransaction.query()
|
const transactions = await AccountTransaction.query()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { omit, sumBy } from 'lodash';
|
import { omit, sumBy } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
import {
|
import {
|
||||||
EventDispatcher,
|
EventDispatcher,
|
||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
@@ -21,7 +22,8 @@ import {
|
|||||||
IPaginationMeta,
|
IPaginationMeta,
|
||||||
IFilterMeta,
|
IFilterMeta,
|
||||||
IBillsFilter,
|
IBillsFilter,
|
||||||
IBillsService
|
IBillsService,
|
||||||
|
IItemEntry,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import ItemsService from 'services/Items/ItemsService';
|
import ItemsService from 'services/Items/ItemsService';
|
||||||
@@ -36,7 +38,9 @@ import { ERRORS } from './constants';
|
|||||||
* @service
|
* @service
|
||||||
*/
|
*/
|
||||||
@Service('Bills')
|
@Service('Bills')
|
||||||
export default class BillsService extends SalesInvoicesCost implements IBillsService {
|
export default class BillsService
|
||||||
|
extends SalesInvoicesCost
|
||||||
|
implements IBillsService {
|
||||||
@Inject()
|
@Inject()
|
||||||
inventoryService: InventoryService;
|
inventoryService: InventoryService;
|
||||||
|
|
||||||
@@ -167,6 +171,29 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default cost account to the bill entries.
|
||||||
|
*/
|
||||||
|
setBillEntriesDefaultAccounts(tenantId: number) {
|
||||||
|
return async (entries: IItemEntry[]) => {
|
||||||
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const entriesItemsIds = entries.map((e) => e.itemId);
|
||||||
|
const items = await Item.query().whereIn('id', entriesItemsIds);
|
||||||
|
|
||||||
|
return entries.map((entry) => {
|
||||||
|
const item = items.find((i) => i.id === entry.itemId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
...(item.type !== 'inventory' && {
|
||||||
|
costAccountId: entry.costAccountId || item.costAccountId,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts create bill DTO to model.
|
* Converts create bill DTO to model.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -182,11 +209,7 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
) {
|
) {
|
||||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const entries = billDTO.entries.map((entry) => ({
|
const amount = sumBy(billDTO.entries, (e) => ItemEntry.calcAmount(e));
|
||||||
...entry,
|
|
||||||
amount: ItemEntry.calcAmount(entry),
|
|
||||||
}));
|
|
||||||
const amount = sumBy(entries, 'amount');
|
|
||||||
|
|
||||||
// Bill number from DTO or from auto-increment.
|
// Bill number from DTO or from auto-increment.
|
||||||
const billNumber = billDTO.billNumber || oldBill?.billNumber;
|
const billNumber = billDTO.billNumber || oldBill?.billNumber;
|
||||||
@@ -196,6 +219,15 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
tenantId,
|
tenantId,
|
||||||
billDTO.vendorId
|
billDTO.vendorId
|
||||||
);
|
);
|
||||||
|
const initialEntries = billDTO.entries.map((entry) => ({
|
||||||
|
reference_type: 'Bill',
|
||||||
|
...omit(entry, ['amount']),
|
||||||
|
}));
|
||||||
|
const entries = await composeAsync(
|
||||||
|
// Sets the default cost account to the bill entries.
|
||||||
|
this.setBillEntriesDefaultAccounts(tenantId)
|
||||||
|
)(initialEntries);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...formatDateFields(omit(billDTO, ['open', 'entries']), [
|
...formatDateFields(omit(billDTO, ['open', 'entries']), [
|
||||||
'billDate',
|
'billDate',
|
||||||
@@ -204,10 +236,7 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
amount,
|
amount,
|
||||||
currencyCode: vendor.currencyCode,
|
currencyCode: vendor.currencyCode,
|
||||||
billNumber,
|
billNumber,
|
||||||
entries: entries.map((entry) => ({
|
entries,
|
||||||
reference_type: 'Bill',
|
|
||||||
...omit(entry, ['amount']),
|
|
||||||
})),
|
|
||||||
// Avoid rewrite the open date in edit mode when already opened.
|
// Avoid rewrite the open date in edit mode when already opened.
|
||||||
...(billDTO.open &&
|
...(billDTO.open &&
|
||||||
!oldBill?.openedAt && {
|
!oldBill?.openedAt && {
|
||||||
@@ -239,15 +268,6 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
): Promise<IBill> {
|
): Promise<IBill> {
|
||||||
const { billRepository } = this.tenancy.repositories(tenantId);
|
const { billRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
this.logger.info('[bill] trying to create a new bill', {
|
|
||||||
tenantId,
|
|
||||||
billDTO,
|
|
||||||
});
|
|
||||||
const billObj = await this.billDTOToModel(
|
|
||||||
tenantId,
|
|
||||||
billDTO,
|
|
||||||
authorizedUser
|
|
||||||
);
|
|
||||||
// Retrieve vendor or throw not found service error.
|
// Retrieve vendor or throw not found service error.
|
||||||
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
||||||
|
|
||||||
@@ -264,8 +284,18 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
tenantId,
|
tenantId,
|
||||||
billDTO.entries
|
billDTO.entries
|
||||||
);
|
);
|
||||||
|
this.logger.info('[bill] trying to create a new bill', {
|
||||||
|
tenantId,
|
||||||
|
billDTO,
|
||||||
|
});
|
||||||
|
// Transform the bill DTO to model object.
|
||||||
|
const billObj = await this.billDTOToModel(
|
||||||
|
tenantId,
|
||||||
|
billDTO,
|
||||||
|
authorizedUser
|
||||||
|
);
|
||||||
// Inserts the bill graph object to the storage.
|
// Inserts the bill graph object to the storage.
|
||||||
const bill = await billRepository.upsertGraph({ ...billObj });
|
const bill = await billRepository.upsertGraph(billObj);
|
||||||
|
|
||||||
// Triggers `onBillCreated` event.
|
// Triggers `onBillCreated` event.
|
||||||
await this.eventDispatcher.dispatch(events.bill.onCreated, {
|
await this.eventDispatcher.dispatch(events.bill.onCreated, {
|
||||||
@@ -309,13 +339,6 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
this.logger.info('[bill] trying to edit bill.', { tenantId, billId });
|
this.logger.info('[bill] trying to edit bill.', { tenantId, billId });
|
||||||
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
||||||
|
|
||||||
// Transforms the bill DTO object to model object.
|
|
||||||
const billObj = await this.billDTOToModel(
|
|
||||||
tenantId,
|
|
||||||
billDTO,
|
|
||||||
authorizedUser,
|
|
||||||
oldBill
|
|
||||||
);
|
|
||||||
// Retrieve vendor details or throw not found service error.
|
// Retrieve vendor details or throw not found service error.
|
||||||
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
||||||
|
|
||||||
@@ -340,6 +363,13 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
tenantId,
|
tenantId,
|
||||||
billDTO.entries
|
billDTO.entries
|
||||||
);
|
);
|
||||||
|
// Transforms the bill DTO to model object.
|
||||||
|
const billObj = await this.billDTOToModel(
|
||||||
|
tenantId,
|
||||||
|
billDTO,
|
||||||
|
authorizedUser,
|
||||||
|
oldBill
|
||||||
|
);
|
||||||
// Update the bill transaction.
|
// Update the bill transaction.
|
||||||
const bill = await billRepository.upsertGraph({
|
const bill = await billRepository.upsertGraph({
|
||||||
id: billId,
|
id: billId,
|
||||||
@@ -498,8 +528,8 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the inventory transactions from the given bill input.
|
* Records the inventory transactions from the given bill input.
|
||||||
* @param {Bill} bill - Bill model object.
|
* @param {Bill} bill - Bill model object.
|
||||||
* @param {number} billId - Bill id.
|
* @param {number} billId - Bill id.
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async recordInventoryTransactions(
|
public async recordInventoryTransactions(
|
||||||
@@ -507,19 +537,25 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
bill: IBill,
|
bill: IBill,
|
||||||
override?: boolean
|
override?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// Loads the inventory items entries of the given sale invoice.
|
||||||
|
const inventoryEntries = await this.itemsEntriesService.filterInventoryEntries(
|
||||||
|
tenantId,
|
||||||
|
bill.entries
|
||||||
|
);
|
||||||
|
const transaction = {
|
||||||
|
transactionId: bill.id,
|
||||||
|
transactionType: 'Bill',
|
||||||
|
|
||||||
|
date: bill.billDate,
|
||||||
|
direction: 'IN',
|
||||||
|
entries: inventoryEntries,
|
||||||
|
createdAt: bill.createdAt,
|
||||||
|
};
|
||||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
bill.id,
|
transaction,
|
||||||
'Bill',
|
|
||||||
bill.billDate,
|
|
||||||
'IN',
|
|
||||||
override
|
override
|
||||||
);
|
);
|
||||||
// Triggers `onInventoryTransactionsCreated` event.
|
|
||||||
this.eventDispatcher.dispatch(events.bill.onInventoryTransactionsCreated, {
|
|
||||||
tenantId,
|
|
||||||
bill,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -582,10 +618,7 @@ export default class BillsService extends SalesInvoicesCost implements IBillsSer
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} vendorId - Vendor id.
|
* @param {number} vendorId - Vendor id.
|
||||||
*/
|
*/
|
||||||
public async validateVendorHasNoBills(
|
public async validateVendorHasNoBills(tenantId: number, vendorId: number) {
|
||||||
tenantId: number,
|
|
||||||
vendorId: number
|
|
||||||
) {
|
|
||||||
const { Bill } = this.tenancy.models(tenantId);
|
const { Bill } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const bills = await Bill.query().where('vendor_id', vendorId);
|
const bills = await Bill.query().where('vendor_id', vendorId);
|
||||||
|
|||||||
@@ -678,8 +678,13 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
|||||||
credit: 0,
|
credit: 0,
|
||||||
referenceId: paymentReceive.id,
|
referenceId: paymentReceive.id,
|
||||||
referenceType: 'PaymentReceive',
|
referenceType: 'PaymentReceive',
|
||||||
|
|
||||||
|
transactionNumber: paymentReceive.paymentReceiveNo,
|
||||||
|
referenceNumber: paymentReceive.referenceNo,
|
||||||
|
|
||||||
date: paymentReceive.paymentDate,
|
date: paymentReceive.paymentDate,
|
||||||
userId: authorizedUserId,
|
userId: authorizedUserId,
|
||||||
|
createdAt: paymentReceive.createdAt,
|
||||||
};
|
};
|
||||||
if (override) {
|
if (override) {
|
||||||
const transactions = await transactionsRepository.journal({
|
const transactions = await transactionsRepository.journal({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { omit, sumBy, join } from 'lodash';
|
import { omit, sumBy, join, entries } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
import {
|
import {
|
||||||
EventDispatcher,
|
EventDispatcher,
|
||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
@@ -15,7 +16,7 @@ import {
|
|||||||
ISystemUser,
|
ISystemUser,
|
||||||
IItem,
|
IItem,
|
||||||
IItemEntry,
|
IItemEntry,
|
||||||
ISalesInvoicesService
|
ISalesInvoicesService,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||||
@@ -181,6 +182,30 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the cost/sell accounts to the invoice entries.
|
||||||
|
*/
|
||||||
|
setInvoiceEntriesDefaultAccounts(tenantId: number) {
|
||||||
|
return async (entries: IItemEntry[]) => {
|
||||||
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const entriesItemsIds = entries.map((e) => e.itemId);
|
||||||
|
const items = await Item.query().whereIn('id', entriesItemsIds);
|
||||||
|
|
||||||
|
return entries.map((entry) => {
|
||||||
|
const item = items.find((i) => i.id === entry.itemId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
sellAccountId: entry.sellAccountId || item.sellAccountId,
|
||||||
|
...(item.type === 'inventory' && {
|
||||||
|
costAccountId: entry.costAccountId || item.costAccountId,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the create DTO to invoice object model.
|
* Transformes the create DTO to invoice object model.
|
||||||
* @param {ISaleInvoiceCreateDTO} saleInvoiceDTO - Sale invoice DTO.
|
* @param {ISaleInvoiceCreateDTO} saleInvoiceDTO - Sale invoice DTO.
|
||||||
@@ -212,6 +237,16 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
// Validate the invoice is required.
|
// Validate the invoice is required.
|
||||||
this.validateInvoiceNoRequire(invoiceNo);
|
this.validateInvoiceNoRequire(invoiceNo);
|
||||||
|
|
||||||
|
const initialEntries = saleInvoiceDTO.entries.map((entry) => ({
|
||||||
|
referenceType: 'SaleInvoice',
|
||||||
|
...entry,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const entries = await composeAsync(
|
||||||
|
// Sets default cost and sell account to invoice items entries.
|
||||||
|
this.setInvoiceEntriesDefaultAccounts(tenantId)
|
||||||
|
)(initialEntries);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...formatDateFields(
|
...formatDateFields(
|
||||||
omit(saleInvoiceDTO, ['delivered', 'entries', 'fromEstimateId']),
|
omit(saleInvoiceDTO, ['delivered', 'entries', 'fromEstimateId']),
|
||||||
@@ -227,10 +262,7 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
// Avoid override payment amount in edit mode.
|
// Avoid override payment amount in edit mode.
|
||||||
...(!oldSaleInvoice && { paymentAmount: 0 }),
|
...(!oldSaleInvoice && { paymentAmount: 0 }),
|
||||||
...(invoiceNo ? { invoiceNo } : {}),
|
...(invoiceNo ? { invoiceNo } : {}),
|
||||||
entries: saleInvoiceDTO.entries.map((entry) => ({
|
entries,
|
||||||
referenceType: 'SaleInvoice',
|
|
||||||
...entry,
|
|
||||||
})),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,23 +291,11 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
): Promise<ISaleInvoice> {
|
): Promise<ISaleInvoice> {
|
||||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Transform DTO object to model object.
|
|
||||||
const saleInvoiceObj = await this.transformDTOToModel(
|
|
||||||
tenantId,
|
|
||||||
saleInvoiceDTO
|
|
||||||
);
|
|
||||||
// Validate customer existance.
|
// Validate customer existance.
|
||||||
await this.customersService.getCustomerByIdOrThrowError(
|
await this.customersService.getCustomerByIdOrThrowError(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.customerId
|
saleInvoiceDTO.customerId
|
||||||
);
|
);
|
||||||
// Validate sale invoice number uniquiness.
|
|
||||||
if (saleInvoiceObj.invoiceNo) {
|
|
||||||
await this.validateInvoiceNumberUnique(
|
|
||||||
tenantId,
|
|
||||||
saleInvoiceObj.invoiceNo
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Validate the from estimate id exists on the storage.
|
// Validate the from estimate id exists on the storage.
|
||||||
if (saleInvoiceDTO.fromEstimateId) {
|
if (saleInvoiceDTO.fromEstimateId) {
|
||||||
const fromEstimate = await this.saleEstimatesService.getSaleEstimateOrThrowError(
|
const fromEstimate = await this.saleEstimatesService.getSaleEstimateOrThrowError(
|
||||||
@@ -295,11 +315,21 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.entries
|
saleInvoiceDTO.entries
|
||||||
);
|
);
|
||||||
|
// Transform DTO object to model object.
|
||||||
|
const saleInvoiceObj = await this.transformDTOToModel(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO
|
||||||
|
);
|
||||||
|
// Validate sale invoice number uniquiness.
|
||||||
|
if (saleInvoiceObj.invoiceNo) {
|
||||||
|
await this.validateInvoiceNumberUnique(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceObj.invoiceNo
|
||||||
|
);
|
||||||
|
}
|
||||||
this.logger.info('[sale_invoice] inserting sale invoice to the storage.');
|
this.logger.info('[sale_invoice] inserting sale invoice to the storage.');
|
||||||
const saleInvoice = await saleInvoiceRepository.upsertGraph({
|
const saleInvoice = await saleInvoiceRepository.upsertGraph(saleInvoiceObj);
|
||||||
...saleInvoiceObj,
|
|
||||||
});
|
|
||||||
// Triggers the event `onSaleInvoiceCreated`.
|
// Triggers the event `onSaleInvoiceCreated`.
|
||||||
await this.eventDispatcher.dispatch(events.saleInvoice.onCreated, {
|
await this.eventDispatcher.dispatch(events.saleInvoice.onCreated, {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -337,25 +367,11 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
// Transform DTO object to model object.
|
|
||||||
const saleInvoiceObj = await this.transformDTOToModel(
|
|
||||||
tenantId,
|
|
||||||
saleInvoiceDTO,
|
|
||||||
oldSaleInvoice
|
|
||||||
);
|
|
||||||
// Validate customer existance.
|
// Validate customer existance.
|
||||||
await this.customersService.getCustomerByIdOrThrowError(
|
await this.customersService.getCustomerByIdOrThrowError(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.customerId
|
saleInvoiceDTO.customerId
|
||||||
);
|
);
|
||||||
// Validate sale invoice number uniquiness.
|
|
||||||
if (saleInvoiceDTO.invoiceNo) {
|
|
||||||
await this.validateInvoiceNumberUnique(
|
|
||||||
tenantId,
|
|
||||||
saleInvoiceDTO.invoiceNo,
|
|
||||||
saleInvoiceId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Validate items ids existance.
|
// Validate items ids existance.
|
||||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -373,6 +389,20 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
saleInvoiceDTO.entries
|
saleInvoiceDTO.entries
|
||||||
);
|
);
|
||||||
|
// Transform DTO object to model object.
|
||||||
|
const saleInvoiceObj = await this.transformDTOToModel(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO,
|
||||||
|
oldSaleInvoice
|
||||||
|
);
|
||||||
|
// Validate sale invoice number uniquiness.
|
||||||
|
if (saleInvoiceObj.invoiceNo) {
|
||||||
|
await this.validateInvoiceNumberUnique(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO.invoiceNo,
|
||||||
|
saleInvoiceId
|
||||||
|
);
|
||||||
|
}
|
||||||
// Validate the invoice amount is not smaller than the invoice payment amount.
|
// Validate the invoice amount is not smaller than the invoice payment amount.
|
||||||
this.validateInvoiceAmountBiggerPaymentAmount(
|
this.validateInvoiceAmountBiggerPaymentAmount(
|
||||||
saleInvoiceObj.balance,
|
saleInvoiceObj.balance,
|
||||||
@@ -445,7 +475,8 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Retrieve the given sale invoice with associated entries or throw not found error.
|
// Retrieve the given sale invoice with associated entries
|
||||||
|
// or throw not found error.
|
||||||
const oldSaleInvoice = await this.getInvoiceOrThrowError(
|
const oldSaleInvoice = await this.getInvoiceOrThrowError(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
@@ -497,12 +528,23 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
|||||||
saleInvoice: ISaleInvoice,
|
saleInvoice: ISaleInvoice,
|
||||||
override?: boolean
|
override?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// Loads the inventory items entries of the given sale invoice.
|
||||||
|
const inventoryEntries = await this.itemsEntriesService.filterInventoryEntries(
|
||||||
|
tenantId,
|
||||||
|
saleInvoice.entries
|
||||||
|
);
|
||||||
|
const transaction = {
|
||||||
|
transactionId: saleInvoice.id,
|
||||||
|
transactionType: 'SaleInvoice',
|
||||||
|
|
||||||
|
date: saleInvoice.invoiceDate,
|
||||||
|
direction: 'OUT',
|
||||||
|
entries: inventoryEntries,
|
||||||
|
createdAt: saleInvoice.created_at,
|
||||||
|
};
|
||||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice.id,
|
transaction,
|
||||||
'SaleInvoice',
|
|
||||||
saleInvoice.invoiceDate,
|
|
||||||
'OUT',
|
|
||||||
override
|
override
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Container, Service, Inject } from 'typedi';
|
import { Container, Service, Inject } from 'typedi';
|
||||||
import { chain } from 'lodash';
|
import { chain, groupBy } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
import InventoryService from 'services/Inventory/Inventory';
|
import InventoryService from 'services/Inventory/Inventory';
|
||||||
@@ -108,6 +108,20 @@ export default class SaleInvoicesCost {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grpups by transaction type and id the inventory transactions.
|
||||||
|
* @param {IInventoryTransaction} invTransactions
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
inventoryTransactionsGroupByType(
|
||||||
|
invTransactions: { transactionType: string; transactionId: number }[]
|
||||||
|
): { transactionType: string; transactionId: number }[][] {
|
||||||
|
return chain(invTransactions)
|
||||||
|
.groupBy((t) => `${t.transactionType}-${t.transactionId}`)
|
||||||
|
.values()
|
||||||
|
.value();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes journal entries from sales invoices.
|
* Writes journal entries from sales invoices.
|
||||||
* @param {number} tenantId - The tenant id.
|
* @param {number} tenantId - The tenant id.
|
||||||
@@ -124,25 +138,28 @@ export default class SaleInvoicesCost {
|
|||||||
|
|
||||||
const inventoryCostLotTrans = await InventoryCostLotTracker.query()
|
const inventoryCostLotTrans = await InventoryCostLotTracker.query()
|
||||||
.where('direction', 'OUT')
|
.where('direction', 'OUT')
|
||||||
.modify('groupedEntriesCost')
|
|
||||||
.modify('filterDateRange', startingDate)
|
.modify('filterDateRange', startingDate)
|
||||||
.orderBy('date', 'ASC')
|
.orderBy('date', 'ASC')
|
||||||
.where('cost', '>', 0)
|
.where('cost', '>', 0)
|
||||||
.withGraphFetched('item');
|
.withGraphFetched('item')
|
||||||
|
.withGraphFetched('itemEntry');
|
||||||
|
|
||||||
const accountsDepGraph = await accountRepository.getDependencyGraph();
|
const accountsDepGraph = await accountRepository.getDependencyGraph();
|
||||||
|
|
||||||
const journal = new JournalPoster(tenantId, accountsDepGraph);
|
const journal = new JournalPoster(tenantId, accountsDepGraph);
|
||||||
const journalCommands = new JournalCommands(journal);
|
const journalCommands = new JournalCommands(journal);
|
||||||
|
|
||||||
|
// Groups the inventory cost lots transactions.
|
||||||
|
const inventoryTransactions = this.inventoryTransactionsGroupByType(
|
||||||
|
inventoryCostLotTrans
|
||||||
|
);
|
||||||
if (override) {
|
if (override) {
|
||||||
await journalCommands.revertInventoryCostJournalEntries(startingDate);
|
await journalCommands.revertInventoryCostJournalEntries(startingDate);
|
||||||
}
|
}
|
||||||
inventoryCostLotTrans.forEach(
|
inventoryTransactions.forEach((inventoryLots) => {
|
||||||
(inventoryCostLot: IInventoryLotCost & { item: IItem }) => {
|
journalCommands.saleInvoiceInventoryCost(inventoryLots);
|
||||||
journalCommands.saleInvoiceInventoryCost(inventoryCostLot);
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveEntries(),
|
journal.saveEntries(),
|
||||||
|
|||||||
@@ -51,15 +51,9 @@ export default class SaleInvoiceSubscriber {
|
|||||||
saleInvoice,
|
saleInvoice,
|
||||||
authorizedUser,
|
authorizedUser,
|
||||||
}) {
|
}) {
|
||||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
|
||||||
|
|
||||||
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
|
||||||
saleInvoiceId,
|
|
||||||
'entries.item'
|
|
||||||
);
|
|
||||||
await this.saleInvoicesService.writesIncomeJournalEntries(
|
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceWithItems,
|
saleInvoice,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,16 @@ export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
|||||||
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var increment = (n) => {
|
||||||
|
return () => {
|
||||||
|
n += 1;
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
increment,
|
||||||
hashPassword,
|
hashPassword,
|
||||||
origin,
|
origin,
|
||||||
dateRangeCollection,
|
dateRangeCollection,
|
||||||
|
|||||||
Reference in New Issue
Block a user