mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
- feat: Optimize tenancy software architecture.
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { difference, omit } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ItemEntry } from '@/models';
|
||||
|
||||
@Service()
|
||||
export default class HasItemEntries {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
/**
|
||||
* Patch items entries to the storage.
|
||||
*
|
||||
@@ -9,15 +15,17 @@ export default class HasItemEntries {
|
||||
* @param {Array} oldEntries -
|
||||
* @param {String} referenceType -
|
||||
* @param {String|Number} referenceId -
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async patchItemsEntries(
|
||||
async patchItemsEntries(
|
||||
tenantId: number,
|
||||
newEntries: Array<any>,
|
||||
oldEntries: Array<any>,
|
||||
referenceType: string,
|
||||
referenceId: string|number
|
||||
) {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const entriesHasIds = newEntries.filter((entry) => entry.id);
|
||||
const entriesHasNoIds = newEntries.filter((entry) => !entry.id);
|
||||
const entriesIds = entriesHasIds.map(entry => entry.id);
|
||||
@@ -31,15 +39,13 @@ export default class HasItemEntries {
|
||||
entriesIds,
|
||||
);
|
||||
if (entriesIdsShouldDelete.length > 0) {
|
||||
const deleteOper = ItemEntry.tenant()
|
||||
.query()
|
||||
const deleteOper = ItemEntry.query()
|
||||
.whereIn('id', entriesIdsShouldDelete)
|
||||
.delete();
|
||||
opers.push(deleteOper);
|
||||
}
|
||||
entriesHasIds.forEach((entry) => {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
const updateOper = ItemEntry.query()
|
||||
.where('id', entry.id)
|
||||
.update({
|
||||
...omit(entry, excludeAttrs),
|
||||
@@ -47,8 +53,7 @@ export default class HasItemEntries {
|
||||
opers.push(updateOper);
|
||||
});
|
||||
entriesHasNoIds.forEach((entry) => {
|
||||
const insertOper = ItemEntry.tenant()
|
||||
.query()
|
||||
const insertOper = ItemEntry.query()
|
||||
.insert({
|
||||
reference_id: referenceId,
|
||||
reference_type: referenceType,
|
||||
@@ -59,7 +64,7 @@ export default class HasItemEntries {
|
||||
return Promise.all([...opers]);
|
||||
}
|
||||
|
||||
static filterNonInventoryEntries(entries: [], items: []) {
|
||||
filterNonInventoryEntries(entries: [], items: []) {
|
||||
const nonInventoryItems = items.filter((item: any) => item.type !== 'inventory');
|
||||
const nonInventoryItemsIds = nonInventoryItems.map((i: any) => i.id);
|
||||
|
||||
@@ -69,7 +74,7 @@ export default class HasItemEntries {
|
||||
));
|
||||
}
|
||||
|
||||
static filterInventoryEntries(entries: [], items: []) {
|
||||
filterInventoryEntries(entries: [], items: []) {
|
||||
const inventoryItems = items.filter((item: any) => item.type === 'inventory');
|
||||
const inventoryItemsIds = inventoryItems.map((i: any) => i.id);
|
||||
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
import { Account, AccountTransaction } from '@/models';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
|
||||
@Service()
|
||||
export default class JournalPosterService {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
/**
|
||||
* Deletes the journal transactions that associated to the given reference id.
|
||||
* @param {number} tenantId - The given tenant id.
|
||||
* @param {number} referenceId - The transaction reference id.
|
||||
* @param {string} referenceType - The transaction reference type.
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async deleteJournalTransactions(referenceId, referenceType) {
|
||||
async deleteJournalTransactions(
|
||||
tenantId: number,
|
||||
referenceId: number,
|
||||
referenceType: string
|
||||
) {
|
||||
const { Account, AccountTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
const transactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
.whereIn('reference_type', [referenceType])
|
||||
@@ -1,13 +1,7 @@
|
||||
import { omit, sumBy, chain } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
AccountTransaction,
|
||||
PaymentReceive,
|
||||
PaymentReceiveEntry,
|
||||
SaleInvoice,
|
||||
Customer,
|
||||
Account,
|
||||
} from '@/models';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IPaymentReceiveOTD } from '@/interfaces';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
@@ -15,23 +9,41 @@ import JournalPosterService from '@/services/Sales/JournalPosterService';
|
||||
import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries';
|
||||
import PaymentReceiveEntryRepository from '@/repositories/PaymentReceiveEntryRepository';
|
||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { formatDateFields } from '@/utils';
|
||||
|
||||
/**
|
||||
* Payment receive service.
|
||||
* @service
|
||||
*/
|
||||
@Service()
|
||||
export default class PaymentReceiveService {
|
||||
@Inject()
|
||||
accountsService: AccountsService;
|
||||
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
journalService: JournalPosterService;
|
||||
|
||||
/**
|
||||
* Creates a new payment receive and store it to the storage
|
||||
* with associated invoices payment and journal transactions.
|
||||
* @async
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
*/
|
||||
static async createPaymentReceive(paymentReceive: any) {
|
||||
async createPaymentReceive(tenantId: number, paymentReceive: IPaymentReceiveOTD) {
|
||||
const {
|
||||
PaymentReceive,
|
||||
PaymentReceiveEntry,
|
||||
SaleInvoice,
|
||||
Customer,
|
||||
} = this.tenancy.models(tenantId);
|
||||
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
const storedPaymentReceive = await PaymentReceive.tenant()
|
||||
.query()
|
||||
const storedPaymentReceive = await PaymentReceive.query()
|
||||
.insert({
|
||||
amount: paymentAmount,
|
||||
...formatDateFields(omit(paymentReceive, ['entries']), ['payment_date']),
|
||||
@@ -39,15 +51,13 @@ export default class PaymentReceiveService {
|
||||
const storeOpers: Array<any> = [];
|
||||
|
||||
paymentReceive.entries.forEach((entry: any) => {
|
||||
const oper = PaymentReceiveEntry.tenant()
|
||||
.query()
|
||||
const oper = PaymentReceiveEntry.query()
|
||||
.insert({
|
||||
payment_receive_id: storedPaymentReceive.id,
|
||||
...entry,
|
||||
});
|
||||
// Increment the invoice payment amount.
|
||||
const invoice = SaleInvoice.tenant()
|
||||
.query()
|
||||
const invoice = SaleInvoice.query()
|
||||
.where('id', entry.invoice_id)
|
||||
.increment('payment_amount', entry.payment_amount);
|
||||
|
||||
@@ -59,10 +69,10 @@ export default class PaymentReceiveService {
|
||||
paymentAmount,
|
||||
);
|
||||
// Records the sale invoice journal transactions.
|
||||
const recordJournalTransactions = this.recordPaymentReceiveJournalEntries({
|
||||
id: storedPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
});
|
||||
const recordJournalTransactions = this.recordPaymentReceiveJournalEntries(tenantId,{
|
||||
id: storedPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
});
|
||||
await Promise.all([
|
||||
...storeOpers,
|
||||
customerIncrementOper,
|
||||
@@ -82,19 +92,22 @@ export default class PaymentReceiveService {
|
||||
* - Update the different customer balances.
|
||||
* - Update the different invoice payment amount.
|
||||
* @async
|
||||
* @param {Integer} paymentReceiveId
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {IPaymentReceive} oldPaymentReceive
|
||||
* @param {number} tenantId -
|
||||
* @param {Integer} paymentReceiveId -
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @param {IPaymentReceive} oldPaymentReceive -
|
||||
*/
|
||||
static async editPaymentReceive(
|
||||
async editPaymentReceive(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
paymentReceive: any,
|
||||
oldPaymentReceive: any
|
||||
) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
// Update the payment receive transaction.
|
||||
const updatePaymentReceive = await PaymentReceive.tenant()
|
||||
.query()
|
||||
const updatePaymentReceive = await PaymentReceive.query()
|
||||
.where('id', paymentReceiveId)
|
||||
.update({
|
||||
amount: paymentAmount,
|
||||
@@ -131,6 +144,7 @@ export default class PaymentReceiveService {
|
||||
}
|
||||
// Re-write the journal transactions of the given payment receive.
|
||||
const recordJournalTransactions = this.recordPaymentReceiveJournalEntries(
|
||||
tenantId,
|
||||
{
|
||||
id: oldPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
@@ -147,6 +161,7 @@ export default class PaymentReceiveService {
|
||||
);
|
||||
// Change the difference between the old and new invoice payment amount.
|
||||
const diffInvoicePaymentAmount = this.saveChangeInvoicePaymentAmount(
|
||||
tenantId,
|
||||
oldPaymentReceive.entries,
|
||||
paymentReceive.entries
|
||||
);
|
||||
@@ -169,24 +184,26 @@ export default class PaymentReceiveService {
|
||||
* - Revert the customer balance.
|
||||
* - Revert the payment amount of the associated invoices.
|
||||
* @async
|
||||
* @param {Integer} paymentReceiveId
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Integer} paymentReceiveId - Payment receive id.
|
||||
* @param {IPaymentReceive} paymentReceive - Payment receive object.
|
||||
*/
|
||||
static async deletePaymentReceive(paymentReceiveId: number, paymentReceive: any) {
|
||||
async deletePaymentReceive(tenantId: number, paymentReceiveId: number, paymentReceive: any) {
|
||||
const { PaymentReceive, PaymentReceiveEntry, Customer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Deletes the payment receive transaction.
|
||||
await PaymentReceive.tenant()
|
||||
.query()
|
||||
await PaymentReceive.query()
|
||||
.where('id', paymentReceiveId)
|
||||
.delete();
|
||||
|
||||
// Deletes the payment receive associated entries.
|
||||
await PaymentReceiveEntry.tenant()
|
||||
.query()
|
||||
await PaymentReceiveEntry.query()
|
||||
.where('payment_receive_id', paymentReceiveId)
|
||||
.delete();
|
||||
|
||||
// Delete all associated journal transactions to payment receive transaction.
|
||||
const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions(
|
||||
const deleteTransactionsOper = this.journalService.deleteJournalTransactions(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
'PaymentReceive'
|
||||
);
|
||||
@@ -197,6 +214,7 @@ export default class PaymentReceiveService {
|
||||
);
|
||||
// Revert the invoices payments amount.
|
||||
const revertInvoicesPaymentAmount = this.revertInvoicePaymentAmount(
|
||||
tenantId,
|
||||
paymentReceive.entries.map((entry: any) => ({
|
||||
invoiceId: entry.invoiceId,
|
||||
revertAmount: entry.paymentAmount,
|
||||
@@ -211,11 +229,12 @@ export default class PaymentReceiveService {
|
||||
|
||||
/**
|
||||
* Retrieve the payment receive details of the given id.
|
||||
* @param {Integer} paymentReceiveId
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Integer} paymentReceiveId - Payment receive id.
|
||||
*/
|
||||
static async getPaymentReceive(paymentReceiveId: number) {
|
||||
const paymentReceive = await PaymentReceive.tenant()
|
||||
.query()
|
||||
async getPaymentReceive(tenantId: number, paymentReceiveId: number) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const paymentReceive = await PaymentReceive.query()
|
||||
.where('id', paymentReceiveId)
|
||||
.withGraphFetched('entries.invoice')
|
||||
.first();
|
||||
@@ -226,9 +245,9 @@ export default class PaymentReceiveService {
|
||||
* Retrieve the payment receive details with associated invoices.
|
||||
* @param {Integer} paymentReceiveId
|
||||
*/
|
||||
static async getPaymentReceiveWithInvoices(paymentReceiveId: number) {
|
||||
return PaymentReceive.tenant()
|
||||
.query()
|
||||
async getPaymentReceiveWithInvoices(tenantId: number, paymentReceiveId: number) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
return PaymentReceive.query()
|
||||
.where('id', paymentReceiveId)
|
||||
.withGraphFetched('invoices')
|
||||
.first();
|
||||
@@ -236,11 +255,12 @@ export default class PaymentReceiveService {
|
||||
|
||||
/**
|
||||
* Detarmines whether the payment receive exists on the storage.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Integer} paymentReceiveId
|
||||
*/
|
||||
static async isPaymentReceiveExists(paymentReceiveId: number) {
|
||||
const paymentReceives = await PaymentReceive.tenant()
|
||||
.query()
|
||||
async isPaymentReceiveExists(tenantId: number, paymentReceiveId: number) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const paymentReceives = await PaymentReceive.query()
|
||||
.where('id', paymentReceiveId);
|
||||
return paymentReceives.length > 0;
|
||||
}
|
||||
@@ -248,15 +268,17 @@ export default class PaymentReceiveService {
|
||||
/**
|
||||
* Detarmines the payment receive number existance.
|
||||
* @async
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Integer} paymentReceiveNumber - Payment receive number.
|
||||
* @param {Integer} paymentReceiveId - Payment receive id.
|
||||
*/
|
||||
static async isPaymentReceiveNoExists(
|
||||
async isPaymentReceiveNoExists(
|
||||
tenantId: number,
|
||||
paymentReceiveNumber: string|number,
|
||||
paymentReceiveId: number
|
||||
) {
|
||||
const paymentReceives = await PaymentReceive.tenant()
|
||||
.query()
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const paymentReceives = await PaymentReceive.query()
|
||||
.where('payment_receive_no', paymentReceiveNumber)
|
||||
.onBuild((query) => {
|
||||
if (paymentReceiveId) {
|
||||
@@ -273,21 +295,25 @@ export default class PaymentReceiveService {
|
||||
* --------
|
||||
* - Account receivable -> Debit
|
||||
* - Payment account [current asset] -> Credit
|
||||
*
|
||||
* @async
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {Number} paymentReceiveId
|
||||
*/
|
||||
static async recordPaymentReceiveJournalEntries(
|
||||
async recordPaymentReceiveJournalEntries(
|
||||
tenantId: number,
|
||||
paymentReceive: any,
|
||||
paymentReceiveId?: number
|
||||
) {
|
||||
const { Account, AccountTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
const formattedDate = moment(paymentReceive.payment_date).format('YYYY-MM-DD');
|
||||
const receivableAccount = await AccountsService.getAccountByType(
|
||||
const receivableAccount = await this.accountsService.getAccountByType(
|
||||
tenantId,
|
||||
'accounts_receivable'
|
||||
);
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const accountsDepGraph = await Account.depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
const commonJournal = {
|
||||
debit: 0,
|
||||
@@ -297,8 +323,7 @@ export default class PaymentReceiveService {
|
||||
date: formattedDate,
|
||||
};
|
||||
if (paymentReceiveId) {
|
||||
const transactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
const transactions = await AccountTransaction.query()
|
||||
.whereIn('reference_type', ['PaymentReceive'])
|
||||
.where('reference_id', paymentReceiveId)
|
||||
.withGraphFetched('account.type');
|
||||
@@ -330,15 +355,18 @@ export default class PaymentReceiveService {
|
||||
|
||||
/**
|
||||
* Revert the payment amount of the given invoices ids.
|
||||
* @async
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Array} revertInvoices
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async revertInvoicePaymentAmount(revertInvoices: any[]) {
|
||||
async revertInvoicePaymentAmount(tenantId: number, revertInvoices: any[]) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const opers: Promise<T>[] = [];
|
||||
|
||||
revertInvoices.forEach((revertInvoice) => {
|
||||
const { revertAmount, invoiceId } = revertInvoice;
|
||||
const oper = SaleInvoice.tenant()
|
||||
.query()
|
||||
const oper = SaleInvoice.query()
|
||||
.where('id', invoiceId)
|
||||
.decrement('payment_amount', revertAmount);
|
||||
opers.push(oper);
|
||||
@@ -348,14 +376,18 @@ export default class PaymentReceiveService {
|
||||
|
||||
/**
|
||||
* Saves difference changing between old and new invoice payment amount.
|
||||
* @async
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Array} paymentReceiveEntries
|
||||
* @param {Array} newPaymentReceiveEntries
|
||||
* @return
|
||||
*/
|
||||
static async saveChangeInvoicePaymentAmount(
|
||||
async saveChangeInvoicePaymentAmount(
|
||||
tenantId: number,
|
||||
paymentReceiveEntries: [],
|
||||
newPaymentReceiveEntries: [],
|
||||
) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const opers: Promise<T>[] = [];
|
||||
const newEntriesTable = chain(newPaymentReceiveEntries)
|
||||
.groupBy('invoice_id')
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
import { omit, difference, sumBy, mixin } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { SaleEstimate, ItemEntry } from '@/models';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
import { formatDateFields } from '@/utils';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
/**
|
||||
* Sale estimate service.
|
||||
* @Service
|
||||
*/
|
||||
@Service()
|
||||
export default class SaleEstimateService {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
itemsEntriesService: HasItemsEntries;
|
||||
|
||||
/**
|
||||
* Creates a new estimate with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {void}
|
||||
*/
|
||||
static async createEstimate(estimateDTO: any) {
|
||||
async createEstimate(tenantId: number, estimateDTO: any) {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const amount = sumBy(estimateDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const estimate = {
|
||||
amount: sumBy(estimateDTO.entries, 'amount'),
|
||||
amount,
|
||||
...formatDateFields(estimateDTO, ['estimate_date', 'expiration_date']),
|
||||
};
|
||||
const storedEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
const storedEstimate = await SaleEstimate.query()
|
||||
.insert({
|
||||
...omit(estimate, ['entries']),
|
||||
});
|
||||
const storeEstimateEntriesOpers: any[] = [];
|
||||
|
||||
estimate.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
const oper = ItemEntry.query()
|
||||
.insert({
|
||||
reference_type: 'SaleEstimate',
|
||||
reference_id: storedEstimate.id,
|
||||
@@ -41,27 +54,33 @@ export default class SaleEstimateService {
|
||||
/**
|
||||
* Edit details of the given estimate with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {void}
|
||||
*/
|
||||
static async editEstimate(estimateId: number, estimateDTO: any) {
|
||||
async editEstimate(tenantId: number, estimateId: number, estimateDTO: any) {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
const amount = sumBy(estimateDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
|
||||
const estimate = {
|
||||
amount: sumBy(estimateDTO.entries, 'amount'),
|
||||
amount,
|
||||
...formatDateFields(estimateDTO, ['estimate_date', 'expiration_date']),
|
||||
};
|
||||
const updatedEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
const updatedEstimate = await SaleEstimate.query()
|
||||
.update({
|
||||
...omit(estimate, ['entries']),
|
||||
});
|
||||
const storedEstimateEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
const storedEstimateEntries = await ItemEntry.query()
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate');
|
||||
|
||||
const patchItemsEntries = HasItemsEntries.patchItemsEntries(
|
||||
estimate.entries, storedEstimateEntries, 'SaleEstimate', estimateId
|
||||
const patchItemsEntries = this.itemsEntriesService.patchItemsEntries(
|
||||
tenantId,
|
||||
estimate.entries,
|
||||
storedEstimateEntries,
|
||||
'SaleEstimate',
|
||||
estimateId,
|
||||
);
|
||||
return Promise.all([
|
||||
patchItemsEntries,
|
||||
@@ -71,32 +90,32 @@ export default class SaleEstimateService {
|
||||
/**
|
||||
* Deletes the given estimate id with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {IEstimate} estimateId
|
||||
* @return {void}
|
||||
*/
|
||||
static async deleteEstimate(estimateId: number) {
|
||||
await ItemEntry.tenant()
|
||||
.query()
|
||||
async deleteEstimate(tenantId: number, estimateId: number) {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
await ItemEntry.query()
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate')
|
||||
.delete();
|
||||
|
||||
await SaleEstimate.tenant()
|
||||
.query()
|
||||
await SaleEstimate.query()
|
||||
.where('id', estimateId)
|
||||
.delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the given estimate ID exists.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Numeric} estimateId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isEstimateExists(estimateId: number) {
|
||||
const foundEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
async isEstimateExists(estimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const foundEstimate = await SaleEstimate.query()
|
||||
.where('id', estimateId);
|
||||
return foundEstimate.length !== 0;
|
||||
}
|
||||
@@ -104,16 +123,17 @@ export default class SaleEstimateService {
|
||||
/**
|
||||
* Validates the given estimate entries IDs.
|
||||
* @async
|
||||
* @param {Numeric} estimateId
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Numeric} estimateId - the sale estimate id.
|
||||
* @param {IEstimate} estimate
|
||||
*/
|
||||
static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) {
|
||||
async isEstimateEntriesIDsExists(tenantId: number, estimateId: number, estimate: any) {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
const estimateEntriesIds = estimate.entries
|
||||
.filter((e: any) => e.id)
|
||||
.map((e: any) => e.id);
|
||||
|
||||
const estimateEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
const estimateEntries = await ItemEntry.query()
|
||||
.whereIn('id', estimateEntriesIds)
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate');
|
||||
@@ -128,12 +148,14 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Retrieve the estimate details of the given estimate id.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
* @return {IEstimate}
|
||||
*/
|
||||
static async getEstimate(estimateId: number) {
|
||||
const estimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
async getEstimate(tenantId: number, estimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const estimate = await SaleEstimate.query()
|
||||
.where('id', estimateId)
|
||||
.first();
|
||||
|
||||
@@ -142,11 +164,13 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Retrieve the estimate details with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
*/
|
||||
static async getEstimateWithEntries(estimateId: number) {
|
||||
const estimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
async getEstimateWithEntries(tenantId: number, estimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const estimate = await SaleEstimate.query()
|
||||
.where('id', estimateId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('customer')
|
||||
@@ -157,13 +181,15 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Detarmines the estimate number uniqness.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {String} estimateNumber
|
||||
* @param {Integer} excludeEstimateId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isEstimateNumberUnique(estimateNumber: string, excludeEstimateId: number) {
|
||||
const foundEstimates = await SaleEstimate.tenant()
|
||||
.query()
|
||||
async isEstimateNumberUnique(tenantId: number, estimateNumber: string, excludeEstimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const foundEstimates = await SaleEstimate.query()
|
||||
.onBuild((query: any) => {
|
||||
query.where('estimate_number', estimateNumber);
|
||||
|
||||
|
||||
@@ -1,52 +1,56 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { omit, sumBy, difference, pick, chain } from 'lodash';
|
||||
import {
|
||||
SaleInvoice,
|
||||
AccountTransaction,
|
||||
InventoryTransaction,
|
||||
Account,
|
||||
ItemEntry,
|
||||
Customer,
|
||||
Item,
|
||||
} from '@/models';
|
||||
import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
||||
import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { formatDateFields } from '@/utils';
|
||||
|
||||
/**
|
||||
* Sales invoices service
|
||||
* @service
|
||||
*/
|
||||
@Service()
|
||||
export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
@Inject()
|
||||
itemsEntriesService: HasItemsEntries;
|
||||
|
||||
/**
|
||||
* Creates a new sale invoices and store it to the storage
|
||||
* with associated to entries and journal transactions.
|
||||
* @async
|
||||
* @param {ISaleInvoice}
|
||||
* @param {number} tenantId =
|
||||
* @param {ISaleInvoice} saleInvoiceDTO -
|
||||
* @return {ISaleInvoice}
|
||||
*/
|
||||
static async createSaleInvoice(saleInvoiceDTO: ISaleInvoiceOTD) {
|
||||
const balance = sumBy(saleInvoiceDTO.entries, 'amount');
|
||||
const invLotNumber = await InventoryService.nextLotNumber();
|
||||
async createSaleInvoice(tenantId: number, saleInvoiceDTO: ISaleInvoiceOTD) {
|
||||
const { SaleInvoice, Customer, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const balance = sumBy(saleInvoiceDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const invLotNumber = await this.inventoryService.nextLotNumber(tenantId);
|
||||
|
||||
const saleInvoice: ISaleInvoice = {
|
||||
...formatDateFields(saleInvoiceDTO, ['invoiceDate', 'dueDate']),
|
||||
...formatDateFields(saleInvoiceDTO, ['invoice_date', 'due_date']),
|
||||
balance,
|
||||
paymentAmount: 0,
|
||||
invLotNumber,
|
||||
};
|
||||
const storedInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
const storedInvoice = await SaleInvoice.query()
|
||||
.insert({
|
||||
...omit(saleInvoice, ['entries']),
|
||||
});
|
||||
const opers: Array<any> = [];
|
||||
|
||||
saleInvoice.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
const oper = ItemEntry.query()
|
||||
.insertAndFetch({
|
||||
reference_type: 'SaleInvoice',
|
||||
reference_id: storedInvoice.id,
|
||||
@@ -67,12 +71,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
...opers, incrementOper,
|
||||
]);
|
||||
// Records the inventory transactions for inventory items.
|
||||
await this.recordInventoryTranscactions(
|
||||
saleInvoice, storedInvoice.id
|
||||
);
|
||||
await this.recordInventoryTranscactions(tenantId, saleInvoice, storedInvoice.id);
|
||||
|
||||
// Schedule sale invoice re-compute based on the item cost
|
||||
// method and starting date.
|
||||
await this.scheduleComputeInvoiceItemsCost(storedInvoice.id);
|
||||
await this.scheduleComputeInvoiceItemsCost(tenantId, storedInvoice.id);
|
||||
|
||||
return storedInvoice;
|
||||
}
|
||||
@@ -80,12 +83,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
/**
|
||||
* Edit the given sale invoice.
|
||||
* @async
|
||||
* @param {number} tenantId -
|
||||
* @param {Number} saleInvoiceId -
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
*/
|
||||
static async editSaleInvoice(saleInvoiceId: number, saleInvoiceDTO: any) {
|
||||
const balance = sumBy(saleInvoiceDTO.entries, 'amount');
|
||||
const oldSaleInvoice = await SaleInvoice.tenant().query()
|
||||
async editSaleInvoice(tenantId: number, saleInvoiceId: number, saleInvoiceDTO: any) {
|
||||
const { SaleInvoice, ItemEntry, Customer } = this.tenancy.models(tenantId);
|
||||
|
||||
const balance = sumBy(saleInvoiceDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const oldSaleInvoice = await SaleInvoice.query()
|
||||
.where('id', saleInvoiceId)
|
||||
.first();
|
||||
|
||||
@@ -94,24 +100,22 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
balance,
|
||||
invLotNumber: oldSaleInvoice.invLotNumber,
|
||||
};
|
||||
const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.query()
|
||||
.where('id', saleInvoiceId)
|
||||
.update({
|
||||
...omit(saleInvoice, ['entries', 'invLotNumber']),
|
||||
});
|
||||
// Fetches the sale invoice items entries.
|
||||
const storedEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
const storedEntries = await ItemEntry.query()
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.where('reference_type', 'SaleInvoice');
|
||||
|
||||
// Patch update the sale invoice items entries.
|
||||
const patchItemsEntriesOper = HasItemsEntries.patchItemsEntries(
|
||||
saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId,
|
||||
const patchItemsEntriesOper = this.itemsEntriesService.patchItemsEntries(
|
||||
tenantId, saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId,
|
||||
);
|
||||
// Changes the diff customer balance between old and new amount.
|
||||
const changeCustomerBalanceOper = CustomerRepository.changeDiffBalance(
|
||||
const changeCustomerBalanceOper = Customer.changeDiffBalance(
|
||||
saleInvoice.customer_id,
|
||||
oldSaleInvoice.customerId,
|
||||
balance,
|
||||
@@ -119,7 +123,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
);
|
||||
// Records the inventory transactions for inventory items.
|
||||
const recordInventoryTransOper = this.recordInventoryTranscactions(
|
||||
saleInvoice, saleInvoiceId, true,
|
||||
tenantId, saleInvoice, saleInvoiceId, true,
|
||||
);
|
||||
await Promise.all([
|
||||
patchItemsEntriesOper,
|
||||
@@ -128,23 +132,31 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
]);
|
||||
// Schedule sale invoice re-compute based on the item cost
|
||||
// method and starting date.
|
||||
await this.scheduleComputeInvoiceItemsCost(saleInvoiceId, true);
|
||||
await this.scheduleComputeInvoiceItemsCost(tenantId, saleInvoiceId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given sale invoice with associated entries
|
||||
* and journal transactions.
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId
|
||||
* @param {Number} saleInvoiceId - The given sale invoice id.
|
||||
*/
|
||||
static async deleteSaleInvoice(saleInvoiceId: number) {
|
||||
const oldSaleInvoice = await SaleInvoice.tenant().query()
|
||||
async deleteSaleInvoice(tenantId: number, saleInvoiceId: number) {
|
||||
const {
|
||||
SaleInvoice,
|
||||
ItemEntry,
|
||||
Customer,
|
||||
Account,
|
||||
InventoryTransaction,
|
||||
AccountTransaction,
|
||||
} = this.tenancy.models(tenantId);
|
||||
|
||||
const oldSaleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
await SaleInvoice.tenant().query().where('id', saleInvoiceId).delete();
|
||||
await ItemEntry.tenant()
|
||||
.query()
|
||||
await SaleInvoice.query().where('id', saleInvoiceId).delete();
|
||||
await ItemEntry.query()
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.delete();
|
||||
@@ -153,26 +165,25 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
oldSaleInvoice.customerId,
|
||||
oldSaleInvoice.balance * -1,
|
||||
);
|
||||
const invoiceTransactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
const invoiceTransactions = await AccountTransaction.query()
|
||||
.whereIn('reference_type', ['SaleInvoice'])
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.withGraphFetched('account.type');
|
||||
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const accountsDepGraph = await Account.depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
|
||||
journal.loadEntries(invoiceTransactions);
|
||||
journal.removeEntries();
|
||||
|
||||
const inventoryTransactions = await InventoryTransaction.tenant()
|
||||
.query()
|
||||
const inventoryTransactions = await InventoryTransaction.query()
|
||||
.where('transaction_type', 'SaleInvoice')
|
||||
.where('transaction_id', saleInvoiceId);
|
||||
|
||||
// Revert inventory transactions.
|
||||
const revertInventoryTransactionsOper = this.revertInventoryTransactions(
|
||||
inventoryTransactions
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
);
|
||||
// Await all async operations.
|
||||
await Promise.all([
|
||||
@@ -183,7 +194,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
]);
|
||||
// Schedule sale invoice re-compute based on the item cost
|
||||
// method and starting date.
|
||||
await this.scheduleComputeItemsCost(oldSaleInvoice)
|
||||
await this.scheduleComputeItemsCost(tenantId, oldSaleInvoice)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +203,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {number} saleInvoiceId -
|
||||
* @param {boolean} override -
|
||||
*/
|
||||
static recordInventoryTranscactions(saleInvoice, saleInvoiceId: number, override?: boolean){
|
||||
recordInventoryTranscactions(tenantId: number, saleInvoice, saleInvoiceId: number, override?: boolean){
|
||||
const inventortyTransactions = saleInvoice.entries
|
||||
.map((entry) => ({
|
||||
...pick(entry, ['item_id', 'quantity', 'rate',]),
|
||||
@@ -204,8 +215,8 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
entryId: entry.id,
|
||||
}));
|
||||
|
||||
return InventoryService.recordInventoryTransactions(
|
||||
inventortyTransactions, override,
|
||||
return this.inventoryService.recordInventoryTransactions(
|
||||
tenantId, inventortyTransactions, override,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -214,15 +225,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {string} transactionType
|
||||
* @param {number} transactionId
|
||||
*/
|
||||
static async revertInventoryTransactions(inventoryTransactions: array) {
|
||||
async revertInventoryTransactions(tenantId: number, inventoryTransactions: array) {
|
||||
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
||||
const opers: Promise<[]>[] = [];
|
||||
|
||||
inventoryTransactions.forEach((trans: any) => {
|
||||
switch(trans.direction) {
|
||||
case 'OUT':
|
||||
if (trans.inventoryTransactionId) {
|
||||
const revertRemaining = InventoryTransaction.tenant()
|
||||
.query()
|
||||
const revertRemaining = InventoryTransaction.query()
|
||||
.where('id', trans.inventoryTransactionId)
|
||||
.where('direction', 'OUT')
|
||||
.increment('remaining', trans.quanitity);
|
||||
@@ -231,8 +242,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
}
|
||||
break;
|
||||
case 'IN':
|
||||
const removeRelationOper = InventoryTransaction.tenant()
|
||||
.query()
|
||||
const removeRelationOper = InventoryTransaction.query()
|
||||
.where('inventory_transaction_id', trans.id)
|
||||
.where('direction', 'IN')
|
||||
.update({
|
||||
@@ -250,8 +260,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId
|
||||
*/
|
||||
static async getSaleInvoiceWithEntries(saleInvoiceId: number) {
|
||||
return SaleInvoice.tenant().query()
|
||||
async getSaleInvoiceWithEntries(tenantId: number, saleInvoiceId: number) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
return SaleInvoice.query()
|
||||
.where('id', saleInvoiceId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('customer')
|
||||
@@ -263,10 +274,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {Integer} saleInvoiceId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isSaleInvoiceExists(saleInvoiceId: number) {
|
||||
const foundSaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
async isSaleInvoiceExists(tenantId: number, saleInvoiceId: number) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const foundSaleInvoice = await SaleInvoice.query()
|
||||
.where('id', saleInvoiceId);
|
||||
|
||||
return foundSaleInvoice.length !== 0;
|
||||
}
|
||||
|
||||
@@ -277,12 +289,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {Number} saleInvoiceId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isSaleInvoiceNumberExists(saleInvoiceNumber: string|number, saleInvoiceId: number) {
|
||||
const foundSaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
async isSaleInvoiceNumberExists(
|
||||
tenantId: number,
|
||||
saleInvoiceNumber: string|number,
|
||||
saleInvoiceId: number
|
||||
) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const foundSaleInvoice = await SaleInvoice.query()
|
||||
.onBuild((query: any) => {
|
||||
query.where('invoice_no', saleInvoiceNumber);
|
||||
|
||||
if (saleInvoiceId) {
|
||||
query.whereNot('id', saleInvoiceId);
|
||||
}
|
||||
@@ -296,9 +311,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {Array} invoicesIds
|
||||
* @return {Array}
|
||||
*/
|
||||
static async isInvoicesExist(invoicesIds: Array<number>) {
|
||||
const storedInvoices = await SaleInvoice.tenant()
|
||||
.query()
|
||||
async isInvoicesExist(tenantId: number, invoicesIds: Array<number>) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const storedInvoices = await SaleInvoice.query()
|
||||
.onBuild((builder: any) => {
|
||||
builder.whereIn('id', invoicesIds);
|
||||
return builder;
|
||||
@@ -314,9 +329,13 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async scheduleComputeInvoiceItemsCost(saleInvoiceId: number, override?: boolean) {
|
||||
const saleInvoice: ISaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
async scheduleComputeInvoiceItemsCost(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
override?: boolean
|
||||
) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const saleInvoice: ISaleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('entries.item');
|
||||
|
||||
@@ -326,9 +345,10 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
.uniq().value();
|
||||
|
||||
if (inventoryItemsIds.length === 0) {
|
||||
await this.writeNonInventoryInvoiceJournals(saleInvoice, override);
|
||||
await this.writeNonInventoryInvoiceJournals(tenantId, saleInvoice, override);
|
||||
} else {
|
||||
await this.scheduleComputeItemsCost(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
saleInvoice.invoice_date,
|
||||
);
|
||||
@@ -339,13 +359,14 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* Writes the sale invoice journal entries.
|
||||
* @param {SaleInvoice} saleInvoice -
|
||||
*/
|
||||
static async writeNonInventoryInvoiceJournals(saleInvoice: ISaleInvoice, override: boolean) {
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
async writeNonInventoryInvoiceJournals(tenantId: number, saleInvoice: ISaleInvoice, override: boolean) {
|
||||
const { Account, AccountTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
const accountsDepGraph = await Account.depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
|
||||
if (override) {
|
||||
const oldTransactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
const oldTransactions = await AccountTransaction.query()
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.where('reference_id', saleInvoice.id)
|
||||
.withGraphFetched('account.type');
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Container } from 'typedi';
|
||||
import {
|
||||
SaleInvoice,
|
||||
Account,
|
||||
AccountTransaction,
|
||||
Item,
|
||||
} from '@/models';
|
||||
import { Container, Service, Inject } from 'typedi';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { ISaleInvoice, IItemEntry, IItem } from '@/interfaces';
|
||||
import { ISaleInvoice } from '../../interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ISaleInvoice, IItemEntry } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class SaleInvoicesCost {
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
/**
|
||||
* Schedule sale invoice re-compute based on the item
|
||||
* cost method and starting date.
|
||||
@@ -19,11 +20,16 @@ export default class SaleInvoicesCost {
|
||||
* @param {Date} startingDate - Starting compute cost date.
|
||||
* @return {Promise<Agenda>}
|
||||
*/
|
||||
static async scheduleComputeItemsCost(inventoryItemsIds: number[], startingDate: Date) {
|
||||
async scheduleComputeItemsCost(
|
||||
tenantId: number,
|
||||
inventoryItemsIds: number[],
|
||||
startingDate: Date
|
||||
) {
|
||||
const asyncOpers: Promise<[]>[] = [];
|
||||
|
||||
inventoryItemsIds.forEach((inventoryItemId: number) => {
|
||||
const oper: Promise<[]> = InventoryService.scheduleComputeItemCost(
|
||||
const oper: Promise<[]> = this.inventoryService.scheduleComputeItemCost(
|
||||
tenantId,
|
||||
inventoryItemId,
|
||||
startingDate,
|
||||
);
|
||||
@@ -37,21 +43,22 @@ export default class SaleInvoicesCost {
|
||||
* @param {Date} startingDate
|
||||
* @return {Promise<agenda>}
|
||||
*/
|
||||
static scheduleWriteJournalEntries(startingDate?: Date) {
|
||||
scheduleWriteJournalEntries(tenantId: number, startingDate?: Date) {
|
||||
const agenda = Container.get('agenda');
|
||||
return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
||||
startingDate,
|
||||
startingDate, tenantId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes journal entries from sales invoices.
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Date} startingDate
|
||||
* @param {boolean} override
|
||||
*/
|
||||
static async writeJournalEntries(startingDate: Date, override: boolean) {
|
||||
const salesInvoices = await SaleInvoice.tenant()
|
||||
.query()
|
||||
async writeJournalEntries(tenantId: number, startingDate: Date, override: boolean) {
|
||||
const { AccountTransaction, SaleInvoice, Account } = this.tenancy.models(tenantId);
|
||||
const salesInvoices = await SaleInvoice.query()
|
||||
.onBuild((builder: any) => {
|
||||
builder.modify('filterDateRange', startingDate);
|
||||
builder.orderBy('invoice_date', 'ASC');
|
||||
@@ -59,12 +66,11 @@ export default class SaleInvoicesCost {
|
||||
builder.withGraphFetched('entries.item')
|
||||
builder.withGraphFetched('costTransactions(groupedEntriesCost)');
|
||||
});
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const accountsDepGraph = await Account.depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
|
||||
if (override) {
|
||||
const oldTransactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
const oldTransactions = await AccountTransaction.query()
|
||||
.whereIn('reference_type', ['SaleInvoice'])
|
||||
.onBuild((builder: any) => {
|
||||
builder.modify('filterDateRange', startingDate);
|
||||
@@ -90,7 +96,7 @@ export default class SaleInvoicesCost {
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @param {JournalPoster} journal
|
||||
*/
|
||||
static saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) {
|
||||
saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) {
|
||||
let inventoryTotal: number = 0;
|
||||
const receivableAccount = { id: 10 };
|
||||
const commonEntry = {
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
import { omit, difference, sumBy } from 'lodash';
|
||||
import {
|
||||
SaleReceipt,
|
||||
Account,
|
||||
ItemEntry,
|
||||
} from '@/models';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import JournalPosterService from '@/services/Sales/JournalPosterService';
|
||||
import HasItemEntries from '@/services/Sales/HasItemsEntries';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { formatDateFields } from '@/utils';
|
||||
|
||||
export default class SalesReceipt {
|
||||
@Service()
|
||||
export default class SalesReceiptService {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
journalService: JournalPosterService;
|
||||
|
||||
@Inject()
|
||||
itemsEntriesService: HasItemEntries;
|
||||
|
||||
/**
|
||||
* Creates a new sale receipt with associated entries.
|
||||
* @async
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {Object}
|
||||
*/
|
||||
static async createSaleReceipt(saleReceiptDTO: any) {
|
||||
async createSaleReceipt(tenantId: number, saleReceiptDTO: any) {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const saleReceipt = {
|
||||
amount: sumBy(saleReceiptDTO.entries, 'amount');
|
||||
amount,
|
||||
...formatDateFields(saleReceiptDTO, ['receipt_date'])
|
||||
};
|
||||
const storedSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
const storedSaleReceipt = await SaleReceipt.query()
|
||||
.insert({
|
||||
...omit(saleReceipt, ['entries']),
|
||||
});
|
||||
const storeSaleReceiptEntriesOpers: Array<any> = [];
|
||||
|
||||
saleReceipt.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
const oper = ItemEntry.query()
|
||||
.insert({
|
||||
reference_type: 'SaleReceipt',
|
||||
reference_id: storedSaleReceipt.id,
|
||||
@@ -48,25 +55,30 @@ export default class SalesReceipt {
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {void}
|
||||
*/
|
||||
static async editSaleReceipt(saleReceiptId: number, saleReceiptDTO: any) {
|
||||
async editSaleReceipt(tenantId: number, saleReceiptId: number, saleReceiptDTO: any) {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const saleReceipt = {
|
||||
amount: sumBy(saleReceiptDTO.entries, 'amount'),
|
||||
amount,
|
||||
...formatDateFields(saleReceiptDTO, ['receipt_date'])
|
||||
};
|
||||
const updatedSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
const updatedSaleReceipt = await SaleReceipt.query()
|
||||
.where('id', saleReceiptId)
|
||||
.update({
|
||||
...omit(saleReceipt, ['entries']),
|
||||
});
|
||||
const storedSaleReceiptEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
const storedSaleReceiptEntries = await ItemEntry.query()
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt');
|
||||
|
||||
// Patch sale receipt items entries.
|
||||
const patchItemsEntries = HasItemEntries.patchItemsEntries(
|
||||
saleReceipt.entries, storedSaleReceiptEntries, 'SaleReceipt', saleReceiptId,
|
||||
const patchItemsEntries = this.itemsEntriesService.patchItemsEntries(
|
||||
tenantId,
|
||||
saleReceipt.entries,
|
||||
storedSaleReceiptEntries,
|
||||
'SaleReceipt',
|
||||
saleReceiptId,
|
||||
);
|
||||
return Promise.all([patchItemsEntries]);
|
||||
}
|
||||
@@ -76,20 +88,20 @@ export default class SalesReceipt {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @return {void}
|
||||
*/
|
||||
static async deleteSaleReceipt(saleReceiptId: number) {
|
||||
const deleteSaleReceiptOper = SaleReceipt.tenant()
|
||||
.query()
|
||||
async deleteSaleReceipt(tenantId: number, saleReceiptId: number) {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
const deleteSaleReceiptOper = SaleReceipt.query()
|
||||
.where('id', saleReceiptId)
|
||||
.delete();
|
||||
|
||||
const deleteItemsEntriesOper = ItemEntry.tenant()
|
||||
.query()
|
||||
const deleteItemsEntriesOper = ItemEntry.query()
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt')
|
||||
.delete();
|
||||
|
||||
// Delete all associated journal transactions to payment receive transaction.
|
||||
const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions(
|
||||
const deleteTransactionsOper = this.journalService.deleteJournalTransactions(
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
'SaleReceipt'
|
||||
);
|
||||
@@ -105,9 +117,9 @@ export default class SalesReceipt {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
static async isSaleReceiptExists(saleReceiptId: number) {
|
||||
const foundSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
async isSaleReceiptExists(tenantId: number, saleReceiptId: number) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
const foundSaleReceipt = await SaleReceipt.query()
|
||||
.where('id', saleReceiptId);
|
||||
return foundSaleReceipt.length !== 0;
|
||||
}
|
||||
@@ -117,13 +129,13 @@ export default class SalesReceipt {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
*/
|
||||
static async isSaleReceiptEntriesIDsExists(saleReceiptId: number, saleReceipt: any) {
|
||||
async isSaleReceiptEntriesIDsExists(tenantId: number, saleReceiptId: number, saleReceipt: any) {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
const entriesIDs = saleReceipt.entries
|
||||
.filter((e) => e.id)
|
||||
.map((e) => e.id);
|
||||
|
||||
const storedEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
const storedEntries = await ItemEntry.query()
|
||||
.whereIn('id', entriesIDs)
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt');
|
||||
@@ -140,21 +152,12 @@ export default class SalesReceipt {
|
||||
* Retrieve sale receipt with associated entries.
|
||||
* @param {Integer} saleReceiptId
|
||||
*/
|
||||
static async getSaleReceiptWithEntries(saleReceiptId: number) {
|
||||
const saleReceipt = await SaleReceipt.tenant().query()
|
||||
async getSaleReceiptWithEntries(tenantId: number, saleReceiptId: number) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
const saleReceipt = await SaleReceipt.query()
|
||||
.where('id', saleReceiptId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
return saleReceipt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records journal transactions for sale receipt.
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async _recordJournalTransactions(saleReceipt: any) {
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const journalPoster = new JournalPoster(accountsDepGraph);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user