diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts index ce7b25103..b3129cc19 100644 --- a/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/server/src/api/controllers/Sales/PaymentReceives.ts @@ -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({ diff --git a/server/src/services/Sales/PaymentsReceives.ts b/server/src/services/Sales/PaymentsReceives.ts index f61a80275..fc54d90be 100644 --- a/server/src/services/Sales/PaymentsReceives.ts +++ b/server/src/services/Sales/PaymentsReceives.ts @@ -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 { + 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 diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index 24e5395b7..d965b3da6 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -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, diff --git a/server/src/subscribers/paymentReceives.ts b/server/src/subscribers/paymentReceives.ts index 9f5505e13..9d34df629 100644 --- a/server/src/subscribers/paymentReceives.ts +++ b/server/src/subscribers/paymentReceives.ts @@ -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. */