feat: wip prepard expenses from vendors

This commit is contained in:
Ahmed Bouhuolia
2024-07-24 18:57:51 +02:00
parent b68d180785
commit f3af3843dd
6 changed files with 179 additions and 21 deletions

View File

@@ -111,6 +111,7 @@ export default class BillsPayments extends BaseController {
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
check('amount').exists().isNumeric().toFloat(),
check('payment_account_id').exists().isNumeric().toInt(),
check('payment_number').optional({ nullable: true }).trim().escape(),
check('payment_date').exists(),
@@ -125,6 +126,8 @@ export default class BillsPayments extends BaseController {
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),
check('prepard_expenses_account_id').optional().isNumeric().toInt(),
];
}

View File

@@ -29,6 +29,9 @@ export interface IBillPayment {
localAmount?: number;
branchId?: number;
prepardExpensesAccountId?: number;
isPrepardExpense: boolean;
}
export interface IBillPaymentEntryDTO {

View File

@@ -11,6 +11,8 @@ export default class BillPayment extends mixin(TenantModel, [
CustomViewBaseModel,
ModelSearchable,
]) {
prepardExpensesAccountId: number;
/**
* Table name
*/
@@ -47,6 +49,14 @@ export default class BillPayment extends mixin(TenantModel, [
return BillPaymentSettings;
}
/**
* Detarmines whether the payment is prepard expense.
* @returns {boolean}
*/
get isPrepardExpense() {
return !!this.prepardExpensesAccountId;
}
/**
* Relationship mapping.
*/

View File

@@ -1,8 +1,14 @@
import moment from 'moment';
import { sumBy } from 'lodash';
import { sumBy, chain } from 'lodash';
import { Service, Inject } from 'typedi';
import { Knex } from 'knex';
import { AccountNormal, IBillPayment, ILedgerEntry } from '@/interfaces';
import {
AccountNormal,
IBillPayment,
IBillPaymentEntry,
ILedger,
ILedgerEntry,
} from '@/interfaces';
import Ledger from '@/services/Accounting/Ledger';
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@@ -21,6 +27,7 @@ export class BillPaymentGLEntries {
* @param {number} tenantId
* @param {number} billPaymentId
* @param {Knex.Transaction} trx
* @returns {Promise<void>}
*/
public writePaymentGLEntries = async (
tenantId: number,
@@ -65,6 +72,7 @@ export class BillPaymentGLEntries {
* @param {number} tenantId
* @param {number} billPaymentId
* @param {Knex.Transaction} trx
* @returns {Promise<void>}
*/
public rewritePaymentGLEntries = async (
tenantId: number,
@@ -102,7 +110,7 @@ export class BillPaymentGLEntries {
* @param {IBillPayment} billPayment
* @returns {}
*/
private getPaymentCommonEntry = (billPayment: IBillPayment) => {
private getPaymentCommonEntry = (billPayment: IBillPayment): ILedgerEntry => {
const formattedDate = moment(billPayment.paymentDate).format('YYYY-MM-DD');
return {
@@ -127,7 +135,7 @@ export class BillPaymentGLEntries {
/**
* Calculates the payment total exchange gain/loss.
* @param {IBillPayment} paymentReceive - Payment receive with entries.
* @param {IBillPayment} paymentReceive - Payment receive with entries.
* @returns {number}
*/
private getPaymentExGainOrLoss = (billPayment: IBillPayment): number => {
@@ -141,10 +149,10 @@ export class BillPaymentGLEntries {
/**
* Retrieves the payment exchange gain/loss entries.
* @param {IBillPayment} billPayment -
* @param {number} APAccountId -
* @param {number} gainLossAccountId -
* @param {string} baseCurrency -
* @param {IBillPayment} billPayment -
* @param {number} APAccountId -
* @param {number} gainLossAccountId -
* @param {string} baseCurrency -
* @returns {ILedgerEntry[]}
*/
private getPaymentExGainOrLossEntries = (
@@ -186,7 +194,7 @@ export class BillPaymentGLEntries {
/**
* Retrieves the payment deposit GL entry.
* @param {IBillPayment} billPayment
* @param {IBillPayment} billPayment
* @returns {ILedgerEntry}
*/
private getPaymentGLEntry = (billPayment: IBillPayment): ILedgerEntry => {
@@ -198,6 +206,7 @@ export class BillPaymentGLEntries {
accountId: billPayment.paymentAccountId,
accountNormal: AccountNormal.DEBIT,
index: 2,
indexGroup: 10,
};
};
@@ -226,8 +235,8 @@ export class BillPaymentGLEntries {
/**
* Retrieves the payment GL entries.
* @param {IBillPayment} billPayment
* @param {number} APAccountId
* @param {IBillPayment} billPayment
* @param {number} APAccountId
* @returns {ILedgerEntry[]}
*/
private getPaymentGLEntries = (
@@ -254,10 +263,53 @@ export class BillPaymentGLEntries {
return [paymentEntry, payableEntry, ...exGainLossEntries];
};
/**
*
* BEFORE APPLYING TO PAYMENT TO BILLS.
* -----------------------------------------
* - Cash/Bank - Credit.
* - Prepard Expenses - Debit
*
* AFTER APPLYING BILLS TO PAYMENT.
* -----------------------------------------
* - Prepard Expenses - Credit
* - A/P - Debit
*
* @param {number} APAccountId - A/P account id.
* @param {IBillPayment} billPayment
*/
private getPrepardExpenseGLEntries = (
APAccountId: number,
billPayment: IBillPayment
) => {
const prepardExpenseEntry = this.getPrepardExpenseEntry(billPayment);
const withdrawalEntry = this.getPaymentGLEntry(billPayment);
const paymentLinesEntries = chain(billPayment.entries)
.map((billPaymentEntry) => {
const APEntry = this.getAccountPayablePaymentLineEntry(
APAccountId,
billPayment,
billPaymentEntry
);
const creditPrepardExpenseEntry = this.getCreditPrepardExpenseEntry(
billPayment,
billPaymentEntry
);
return [creditPrepardExpenseEntry, APEntry];
})
.flatten()
.value();
const prepardExpenseEntries = [prepardExpenseEntry, withdrawalEntry];
const combinedEntries = [...prepardExpenseEntries, ...paymentLinesEntries];
return combinedEntries;
};
/**
* Retrieves the bill payment ledger.
* @param {IBillPayment} billPayment
* @param {number} APAccountId
* @param {IBillPayment} billPayment
* @param {number} APAccountId
* @returns {Ledger}
*/
private getBillPaymentLedger = (
@@ -266,12 +318,79 @@ export class BillPaymentGLEntries {
gainLossAccountId: number,
baseCurrency: string
): Ledger => {
const entries = this.getPaymentGLEntries(
billPayment,
APAccountId,
gainLossAccountId,
baseCurrency
);
const entries = billPayment.isPrepardExpense
? this.getPrepardExpenseGLEntries(APAccountId, billPayment)
: this.getPaymentGLEntries(
billPayment,
APAccountId,
gainLossAccountId,
baseCurrency
);
return new Ledger(entries);
};
/**
* Retrieves the prepard expense GL entry.
* @param {IBillPayment} billPayment
* @returns {ILedgerEntry}
*/
private getPrepardExpenseEntry = (
billPayment: IBillPayment
): ILedgerEntry => {
const commonJournal = this.getPaymentCommonEntry(billPayment);
return {
...commonJournal,
debit: billPayment.localAmount,
accountId: billPayment.prepardExpensesAccountId,
accountNormal: AccountNormal.DEBIT,
indexGroup: 10,
index: 1,
};
};
/**
* Retrieves the GL entries of credit prepard expense for the give payment line.
* @param {IBillPayment} billPayment
* @param {IBillPaymentEntry} billPaymentEntry
* @returns {ILedgerEntry}
*/
private getCreditPrepardExpenseEntry = (
billPayment: IBillPayment,
billPaymentEntry: IBillPaymentEntry
) => {
const commonJournal = this.getPaymentCommonEntry(billPayment);
return {
...commonJournal,
credit: billPaymentEntry.paymentAmount,
accountId: billPayment.prepardExpensesAccountId,
accountNormal: AccountNormal.DEBIT,
index: 2,
indexGroup: 20,
};
};
/**
* Retrieves the A/P debit of the payment line.
* @param {number} APAccountId
* @param {IBillPayment} billPayment
* @param {IBillPaymentEntry} billPaymentEntry
* @returns {ILedgerEntry}
*/
private getAccountPayablePaymentLineEntry = (
APAccountId: number,
billPayment: IBillPayment,
billPaymentEntry: IBillPaymentEntry
): ILedgerEntry => {
const commonJournal = this.getPaymentCommonEntry(billPayment);
return {
...commonJournal,
debit: billPaymentEntry.paymentAmount,
accountId: APAccountId,
index: 1,
indexGroup: 20,
};
};
}

View File

@@ -17,6 +17,10 @@ export class PaymentWriteGLEntriesSubscriber {
*/
public attach(bus) {
bus.subscribe(events.billPayment.onCreated, this.handleWriteJournalEntries);
bus.subscribe(
events.billPayment.onPrepardExpensesApplied,
this.handleWritePrepardExpenseGLEntries
);
bus.subscribe(
events.billPayment.onEdited,
this.handleRewriteJournalEntriesOncePaymentEdited
@@ -28,7 +32,8 @@ export class PaymentWriteGLEntriesSubscriber {
}
/**
* Handle bill payment writing journal entries once created.
* Handles bill payment writing journal entries once created.
* @param {IBillPaymentEventCreatedPayload} payload -
*/
private handleWriteJournalEntries = async ({
tenantId,
@@ -44,6 +49,22 @@ export class PaymentWriteGLEntriesSubscriber {
);
};
/**
* Handles rewrite prepard expense GL entries once the bill payment applying to bills.
* @param {IBillPaymentEventCreatedPayload} payload -
*/
private handleWritePrepardExpenseGLEntries = async ({
tenantId,
billPaymentId,
trx,
}: IBillPaymentEventCreatedPayload) => {
await this.billPaymentGLEntries.rewritePaymentGLEntries(
tenantId,
billPaymentId,
trx
);
};
/**
* Handle bill payment re-writing journal entries once the payment transaction be edited.
*/

View File

@@ -23,11 +23,13 @@ export class CommandBillPaymentDTOTransformer {
vendor: IVendor,
oldBillPayment?: IBillPayment
): Promise<IBillPayment> {
const appliedAmount = sumBy(billPaymentDTO.entries, 'paymentAmount');
const initialDTO = {
...formatDateFields(omit(billPaymentDTO, ['attachments']), [
'paymentDate',
]),
amount: sumBy(billPaymentDTO.entries, 'paymentAmount'),
appliedAmount,
currencyCode: vendor.currencyCode,
exchangeRate: billPaymentDTO.exchangeRate || 1,
entries: billPaymentDTO.entries,