import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; import * as R from 'ramda'; import { AccountNormal, IItemEntry, ILedgerEntry, ICreditNote, ILedger, ICreditNoteGLCommonEntry, } from '@/interfaces'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import Ledger from '@/services/Accounting/Ledger'; import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; @Service() export default class CreditNoteGLEntries { @Inject() private tenancy: HasTenancyService; @Inject() private ledgerStorage: LedgerStorageService; /** * Retrieves the credit note GL. * @param {ICreditNote} creditNote * @param {number} receivableAccount * @returns {Ledger} */ private getCreditNoteGLedger = ( creditNote: ICreditNote, receivableAccount: number ): Ledger => { const ledgerEntries = this.getCreditNoteGLEntries( creditNote, receivableAccount ); return new Ledger(ledgerEntries); }; /** * Saves credit note GL entries. * @param {number} tenantId - * @param {ICreditNote} creditNote - Credit note model. * @param {number} payableAccount - Payable account id. * @param {Knex.Transaction} trx */ public saveCreditNoteGLEntries = async ( tenantId: number, creditNote: ICreditNote, payableAccount: number, trx?: Knex.Transaction ): Promise => { const ledger = this.getCreditNoteGLedger(creditNote, payableAccount); await this.ledgerStorage.commit(tenantId, ledger, trx); }; /** * Reverts the credit note associated GL entries. * @param {number} tenantId * @param {number} vendorCreditId * @param {Knex.Transaction} trx */ public revertVendorCreditGLEntries = async ( tenantId: number, creditNoteId: number, trx?: Knex.Transaction ): Promise => { await this.ledgerStorage.deleteByReference( tenantId, creditNoteId, 'CreditNote', trx ); }; /** * Writes vendor credit associated GL entries. * @param {number} tenantId - Tenant id. * @param {number} creditNoteId - Credit note id. * @param {Knex.Transaction} trx - Knex transactions. */ public createVendorCreditGLEntries = async ( tenantId: number, creditNoteId: number, trx?: Knex.Transaction ): Promise => { const { CreditNote } = this.tenancy.models(tenantId); const { accountRepository } = this.tenancy.repositories(tenantId); // Retrieve the credit note with associated entries and items. const creditNoteWithItems = await CreditNote.query(trx) .findById(creditNoteId) .withGraphFetched('entries.item'); // Retreive the the `accounts receivable` account based on the given currency. const ARAccount = await accountRepository.findOrCreateAccountReceivable( creditNoteWithItems.currencyCode ); // Saves the credit note GL entries. await this.saveCreditNoteGLEntries( tenantId, creditNoteWithItems, ARAccount.id, trx ); }; /** * Edits vendor credit associated GL entries. * @param {number} tenantId * @param {number} creditNoteId * @param {Knex.Transaction} trx */ public editVendorCreditGLEntries = async ( tenantId: number, creditNoteId: number, trx?: Knex.Transaction ): Promise => { // Reverts vendor credit GL entries. await this.revertVendorCreditGLEntries(tenantId, creditNoteId, trx); // Creates vendor credit Gl entries. await this.createVendorCreditGLEntries(tenantId, creditNoteId, trx); }; /** * Retrieve the credit note common entry. * @param {ICreditNote} creditNote - * @returns {ICreditNoteGLCommonEntry} */ private getCreditNoteCommonEntry = ( creditNote: ICreditNote ): ICreditNoteGLCommonEntry => { return { date: creditNote.creditNoteDate, userId: creditNote.userId, currencyCode: creditNote.currencyCode, exchangeRate: creditNote.exchangeRate, transactionType: 'CreditNote', transactionId: creditNote.id, transactionNumber: creditNote.creditNoteNumber, referenceNumber: creditNote.referenceNo, createdAt: creditNote.createdAt, indexGroup: 10, credit: 0, debit: 0, branchId: creditNote.branchId, }; }; /** * Retrieves the creidt note A/R entry. * @param {ICreditNote} creditNote - * @param {number} ARAccountId - * @returns {ILedgerEntry} */ private getCreditNoteAREntry = ( creditNote: ICreditNote, ARAccountId: number ): ILedgerEntry => { const commonEntry = this.getCreditNoteCommonEntry(creditNote); return { ...commonEntry, credit: creditNote.localAmount, accountId: ARAccountId, contactId: creditNote.customerId, index: 1, accountNormal: AccountNormal.DEBIT, }; }; /** * Retrieve the credit note item entry. * @param {ICreditNote} creditNote * @param {IItemEntry} entry * @param {number} index * @returns {ILedgerEntry} */ private getCreditNoteItemEntry = R.curry( ( creditNote: ICreditNote, entry: IItemEntry, index: number ): ILedgerEntry => { const commonEntry = this.getCreditNoteCommonEntry(creditNote); const localAmount = entry.amount * creditNote.exchangeRate; return { ...commonEntry, debit: localAmount, accountId: entry.sellAccountId || entry.item.sellAccountId, note: entry.description, index: index + 2, itemId: entry.itemId, itemQuantity: entry.quantity, accountNormal: AccountNormal.CREDIT, }; } ); /** * Retrieve the credit note GL entries. * @param {ICreditNote} creditNote - Credit note. * @param {IAccount} receivableAccount - Receviable account. * @returns {ILedgerEntry[]} - Ledger entries. */ public getCreditNoteGLEntries = ( creditNote: ICreditNote, ARAccountId: number ): ILedgerEntry[] => { const AREntry = this.getCreditNoteAREntry(creditNote, ARAccountId); const getItemEntry = this.getCreditNoteItemEntry(creditNote); const itemsEntries = creditNote.entries.map(getItemEntry); return [AREntry, ...itemsEntries]; }; }