feat: writing journal entries of payment receive.

This commit is contained in:
a.bouhuolia
2021-01-02 18:29:34 +02:00
parent 53ccd56f2b
commit f18ab184e2
4 changed files with 142 additions and 48 deletions

View File

@@ -138,13 +138,14 @@ export default class PaymentReceivesController extends BaseController {
* Records payment receive to the given customer with associated invoices.
*/
async newPaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { tenantId, user } = req;
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
try {
const storedPaymentReceive = await this.paymentReceiveService.createPaymentReceive(
tenantId,
paymentReceive
paymentReceive,
user
);
return res.status(200).send({
id: storedPaymentReceive.id,
@@ -162,7 +163,7 @@ export default class PaymentReceivesController extends BaseController {
* @return {Response}
*/
async editPaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { tenantId, user } = req;
const { id: paymentReceiveId } = req.params;
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
@@ -171,7 +172,8 @@ export default class PaymentReceivesController extends BaseController {
await this.paymentReceiveService.editPaymentReceive(
tenantId,
paymentReceiveId,
paymentReceive
paymentReceive,
user
);
return res.status(200).send({
id: paymentReceiveId,
@@ -188,13 +190,14 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
*/
async deletePaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { tenantId, user } = req;
const { id: paymentReceiveId } = req.params;
try {
await this.paymentReceiveService.deletePaymentReceive(
tenantId,
paymentReceiveId
paymentReceiveId,
user
);
return res.status(200).send({
@@ -223,7 +226,8 @@ export default class PaymentReceivesController extends BaseController {
paymentReceiveInvoices,
} = await this.paymentReceiveService.getPaymentReceive(
tenantId,
paymentReceiveId
paymentReceiveId,
user
);
return res.status(200).send({

View File

@@ -11,13 +11,13 @@ import {
IFilterMeta,
IPaginationMeta,
IPaymentReceive,
IPaymentReceiveDTO,
IPaymentReceiveCreateDTO,
IPaymentReceiveEditDTO,
IPaymentReceiveEntry,
IPaymentReceiveEntryDTO,
IPaymentReceivesFilter,
ISaleInvoice,
ISystemUser,
} from 'interfaces';
import AccountsService from 'services/Accounts/AccountsService';
import JournalPoster from 'services/Accounting/JournalPoster';
@@ -29,6 +29,7 @@ import { formatDateFields, entriesAmountDiff } from 'utils';
import { ServiceError } from 'exceptions';
import CustomersService from 'services/Contacts/CustomersService';
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
import JournalCommands from 'services/Accounting/JournalCommands';
const ERRORS = {
PAYMENT_RECEIVE_NO_EXISTS: 'PAYMENT_RECEIVE_NO_EXISTS',
@@ -270,7 +271,8 @@ export default class PaymentReceiveService {
*/
public async createPaymentReceive(
tenantId: number,
paymentReceiveDTO: IPaymentReceiveCreateDTO
paymentReceiveDTO: IPaymentReceiveCreateDTO,
authorizedUser: ISystemUser
) {
const { PaymentReceive } = this.tenancy.models(tenantId);
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
@@ -323,6 +325,7 @@ export default class PaymentReceiveService {
tenantId,
paymentReceive,
paymentReceiveId: paymentReceive.id,
authorizedUser,
});
this.logger.info('[payment_receive] updated successfully.', {
tenantId,
@@ -350,7 +353,8 @@ export default class PaymentReceiveService {
public async editPaymentReceive(
tenantId: number,
paymentReceiveId: number,
paymentReceiveDTO: IPaymentReceiveEditDTO
paymentReceiveDTO: IPaymentReceiveEditDTO,
authorizedUser: ISystemUser
) {
const { PaymentReceive } = this.tenancy.models(tenantId);
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
@@ -417,6 +421,7 @@ export default class PaymentReceiveService {
paymentReceiveId,
paymentReceive,
oldPaymentReceive,
authorizedUser,
});
this.logger.info('[payment_receive] upserted successfully.', {
tenantId,
@@ -438,7 +443,11 @@ export default class PaymentReceiveService {
* @param {Integer} paymentReceiveId - Payment receive id.
* @param {IPaymentReceive} paymentReceive - Payment receive object.
*/
async deletePaymentReceive(tenantId: number, paymentReceiveId: number) {
async deletePaymentReceive(
tenantId: number,
paymentReceiveId: number,
authorizedUser: ISystemUser
) {
const { PaymentReceive, PaymentReceiveEntry } = this.tenancy.models(
tenantId
);
@@ -460,6 +469,7 @@ export default class PaymentReceiveService {
tenantId,
paymentReceiveId,
oldPaymentReceive,
authorizedUser,
});
this.logger.info('[payment_receive] deleted successfully.', {
tenantId,
@@ -474,7 +484,7 @@ export default class PaymentReceiveService {
*/
public async getPaymentReceive(
tenantId: number,
paymentReceiveId: number
paymentReceiveId: number,
): Promise<{
paymentReceive: IPaymentReceive;
receivableInvoices: ISaleInvoice[];
@@ -595,55 +605,57 @@ export default class PaymentReceiveService {
* --------
* - Account receivable -> Debit
* - Payment account [current asset] -> Credit
* @async
* @param {number} tenantId - Tenant id.
* @param {IPaymentReceive} paymentReceive
* @param {Number} paymentReceiveId
*/
private async recordPaymentReceiveJournalEntries(
public async recordPaymentReceiveJournalEntries(
tenantId: number,
paymentReceive: any,
paymentReceiveId?: number
) {
const { Account, AccountTransaction } = this.tenancy.models(tenantId);
paymentReceive: IPaymentReceive,
authorizedUserId: number,
override: boolean = false
): Promise<void> {
const {
accountRepository,
transactionsRepository,
} = this.tenancy.repositories(tenantId);
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
const formattedDate = moment(paymentReceive.payment_date).format(
'YYYY-MM-DD'
);
const receivableAccount = await this.accountsService.getAccountByType(
tenantId,
'accounts_receivable'
);
const accountsDepGraph = await Account.depGraph().query();
const journal = new JournalPoster(accountsDepGraph);
const paymentAmount = sumBy(paymentReceive.entries, 'paymentAmount');
// Retrieve the receivable account.
const receivableAccount = await accountRepository.findOne({
slug: 'accounts-receivable',
});
// Accounts dependency graph.
const accountsDepGraph = await accountRepository.getDependencyGraph();
const journal = new JournalPoster(tenantId, accountsDepGraph);
const commonJournal = {
debit: 0,
credit: 0,
referenceId: paymentReceive.id,
referenceType: 'PaymentReceive',
date: formattedDate,
date: paymentReceive.paymentDate,
userId: authorizedUserId,
};
if (paymentReceiveId) {
const transactions = await AccountTransaction.query()
.whereIn('reference_type', ['PaymentReceive'])
.where('reference_id', paymentReceiveId)
.withGraphFetched('account.type');
journal.loadEntries(transactions);
if (override) {
const transactions = await transactionsRepository.journal({
referenceType: ['PaymentReceive'],
referenceId: [paymentReceive.id],
});
journal.fromTransactions(transactions);
journal.removeEntries();
}
const creditReceivable = new JournalEntry({
...commonJournal,
credit: paymentAmount,
contactType: 'Customer',
contactId: paymentReceive.customer_id,
contactId: paymentReceive.customerId,
account: receivableAccount.id,
index: 1,
});
const debitDepositAccount = new JournalEntry({
...commonJournal,
debit: paymentAmount,
account: paymentReceive.deposit_account_id,
account: paymentReceive.depositAccountId,
index: 2,
});
journal.credit(creditReceivable);
journal.debit(debitDepositAccount);
@@ -655,6 +667,28 @@ export default class PaymentReceiveService {
]);
}
/**
* Reverts the given payment receive journal entries.
* @param {number} tenantId
* @param {number} paymentReceiveId
*/
async revertPaymentReceiveJournalEntries(
tenantId: number,
paymentReceiveId: number,
) {
const { accountRepository } = this.tenancy.repositories(tenantId);
// Accounts dependency graph.
const accountsDepGraph = await accountRepository.getDependencyGraph();
const journal = new JournalPoster(tenantId, accountsDepGraph);
const commands = new JournalCommands(journal);
await commands.revertJournalEntries(paymentReceiveId, 'PaymentReceive');
await Promise.all([journal.saveBalance(), journal.deleteEntries()]);
}
/**
* Saves difference changing between old and new invoice payment amount.
* @async

View File

@@ -13,7 +13,6 @@ import {
IPaginationMeta,
IFilterMeta,
ISystemUser,
ISystemService,
} from 'interfaces';
import events from 'subscribers/events';
import InventoryService from 'services/Inventory/Inventory';
@@ -239,19 +238,19 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
const saleInvoiceObj = this.transformDTOToModel(
tenantId,
saleInvoiceDTO,
oldSaleInvoice
oldSaleInvoice,
);
// Validate customer existance.
await this.customersService.getCustomerByIdOrThrowError(
tenantId,
saleInvoiceDTO.customerId
saleInvoiceDTO.customerId,
);
// Validate sale invoice number uniquiness.
if (saleInvoiceDTO.invoiceNo) {
await this.validateInvoiceNumberUnique(
tenantId,
saleInvoiceDTO.invoiceNo,
saleInvoiceId
saleInvoiceId,
);
}
// Validate items ids existance.
@@ -262,7 +261,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
// Validate non-sellable entries items.
await this.itemsEntriesService.validateNonSellableEntriesItems(
tenantId,
saleInvoiceDTO.entries
saleInvoiceDTO.entries,
);
// Validate the items entries existance.
await this.itemsEntriesService.validateEntriesIdsExistance(
@@ -502,7 +501,6 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
const { inventoryTransactionRepository } = this.tenancy.repositories(
tenantId
);
// Retrieve the inventory transactions of the given sale invoice.
const oldInventoryTransactions = await inventoryTransactionRepository.find({
transactionId: saleInvoiceId,

View File

@@ -10,7 +10,6 @@ export default class PaymentReceivesSubscriber {
tenancy: TenancyService;
logger: any;
paymentReceivesService: PaymentReceiveService;
settingsService: SettingsService;
constructor() {
@@ -20,6 +19,26 @@ export default class PaymentReceivesSubscriber {
this.settingsService = Container.get(SettingsService);
}
/**
* Handle journal entries writing once the payment receive created.
*/
@On(events.paymentReceive.onCreated)
async handleWriteJournalEntriesOnceCreated({
tenantId,
paymentReceiveId,
paymentReceive,
authorizedUser,
}) {
this.logger.info('[payment_receive] trying to write journal entries.', {
tenantId, paymentReceiveId,
});
await this.paymentReceivesService.recordPaymentReceiveJournalEntries(
tenantId,
paymentReceive,
authorizedUser.id,
);
}
/**
* Handle customer balance decrement once payment receive created.
*/
@@ -75,6 +94,27 @@ export default class PaymentReceivesSubscriber {
);
}
/**
* Handle journal entries writing once the payment receive edited.
*/
@On(events.paymentReceive.onEdited)
async handleOverwriteJournalEntriesOnceEdited({
tenantId,
paymentReceiveId,
paymentReceive,
authorizedUser,
}) {
this.logger.info('[payment_receive] trying to overwrite journal entries.', {
tenantId, paymentReceiveId,
});
await this.paymentReceivesService.recordPaymentReceiveJournalEntries(
tenantId,
paymentReceive,
authorizedUser.id,
true
);
}
/**
* Handle sale invoice increment/decrement payment amount once created, edited or deleted.
*/
@@ -119,6 +159,24 @@ export default class PaymentReceivesSubscriber {
);
}
/**
* Handles revert journal entries once deleted.
*/
@On(events.paymentReceive.onDeleted)
async handleRevertJournalEntriesOnceDeleted({
tenantId,
paymentReceiveId,
}) {
this.logger.info('[payment_receive] trying to revert journal entries.', {
tenantId,
paymentReceiveId,
});
await this.paymentReceivesService.revertPaymentReceiveJournalEntries(
tenantId,
paymentReceiveId
)
}
/**
* Handles increment next number of payment receive once be created.
*/