mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
Compare commits
3 Commits
advanced-p
...
v0.18.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b93cb546f4 | ||
|
|
6d17f9cbeb | ||
|
|
6b6b73b77c |
@@ -126,8 +126,6 @@ export default class BillsPayments extends BaseController {
|
||||
|
||||
check('attachments').isArray().optional(),
|
||||
check('attachments.*.key').exists().isString(),
|
||||
|
||||
check('prepard_expenses_account_id').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -150,9 +150,8 @@ export default class PaymentReceivesController extends BaseController {
|
||||
check('customer_id').exists().isNumeric().toInt(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
check('payment_date').exists(),
|
||||
check('amount').exists().isNumeric().toFloat(),
|
||||
|
||||
check('payment_date').exists(),
|
||||
check('reference_no').optional(),
|
||||
check('deposit_account_id').exists().isNumeric().toInt(),
|
||||
check('payment_receive_no').optional({ nullable: true }).trim().escape(),
|
||||
@@ -160,8 +159,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('entries').isArray(),
|
||||
|
||||
check('entries').isArray({}),
|
||||
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
check('entries.*.index').optional().isNumeric().toInt(),
|
||||
check('entries.*.invoice_id').exists().isNumeric().toInt(),
|
||||
@@ -169,11 +167,6 @@ export default class PaymentReceivesController extends BaseController {
|
||||
|
||||
check('attachments').isArray().optional(),
|
||||
check('attachments.*.key').exists().isString(),
|
||||
|
||||
check('unearned_revenue_account_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -237,4 +237,8 @@ module.exports = {
|
||||
endpoint: process.env.S3_ENDPOINT,
|
||||
bucket: process.env.S3_BUCKET || 'bigcapital-documents',
|
||||
},
|
||||
|
||||
loops: {
|
||||
apiKey: process.env.LOOPS_API_KEY,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.table('payment_receives', (table) => {
|
||||
table.decimal('applied_amount', 13, 3).defaultTo(0);
|
||||
table
|
||||
.integer('unearned_revenue_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.table('payment_receives', (table) => {
|
||||
table.dropColumn('applied_amount');
|
||||
table.dropColumn('unearned_revenue_account_id');
|
||||
});
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.table('bills_payments', (table) => {
|
||||
table.decimal('applied_amount', 13, 3).defaultTo(0);
|
||||
table
|
||||
.integer('prepard_expenses_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.table('bills_payments', (table) => {
|
||||
table.dropColumn('applied_amount');
|
||||
table.dropColumn('prepard_expenses_account_id');
|
||||
});
|
||||
};
|
||||
@@ -166,10 +166,3 @@ export interface IBillOpenedPayload {
|
||||
oldBill: IBill;
|
||||
tenantId: number;
|
||||
}
|
||||
|
||||
|
||||
export interface IBillPrepardExpensesAppliedEventPayload {
|
||||
tenantId: number;
|
||||
billId: number;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,6 @@ export interface IBillPayment {
|
||||
|
||||
localAmount?: number;
|
||||
branchId?: number;
|
||||
|
||||
prepardExpensesAccountId?: number;
|
||||
isPrepardExpense: boolean;
|
||||
}
|
||||
|
||||
export interface IBillPaymentEntryDTO {
|
||||
@@ -41,7 +38,6 @@ export interface IBillPaymentEntryDTO {
|
||||
|
||||
export interface IBillPaymentDTO {
|
||||
vendorId: number;
|
||||
amount: number;
|
||||
paymentAccountId: number;
|
||||
paymentNumber?: string;
|
||||
paymentDate: Date;
|
||||
@@ -51,7 +47,6 @@ export interface IBillPaymentDTO {
|
||||
entries: IBillPaymentEntryDTO[];
|
||||
branchId?: number;
|
||||
attachments?: AttachmentLinkDTO[];
|
||||
prepardExpensesAccountId?: number;
|
||||
}
|
||||
|
||||
export interface IBillReceivePageEntry {
|
||||
@@ -124,11 +119,3 @@ export enum IPaymentMadeAction {
|
||||
Delete = 'Delete',
|
||||
View = 'View',
|
||||
}
|
||||
|
||||
export interface IPaymentPrepardExpensesAppliedEventPayload {
|
||||
tenantId: number;
|
||||
billPaymentId: number;
|
||||
billId: number;
|
||||
appliedAmount: number;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -25,13 +25,8 @@ export interface IPaymentReceive {
|
||||
updatedAt: Date;
|
||||
localAmount?: number;
|
||||
branchId?: number;
|
||||
unearnedRevenueAccountId?: number;
|
||||
}
|
||||
|
||||
interface IPaymentReceivedCommonDTO {
|
||||
unearnedRevenueAccountId?: number;
|
||||
}
|
||||
export interface IPaymentReceiveCreateDTO extends IPaymentReceivedCommonDTO {
|
||||
export interface IPaymentReceiveCreateDTO {
|
||||
customerId: number;
|
||||
paymentDate: Date;
|
||||
amount: number;
|
||||
@@ -46,7 +41,7 @@ export interface IPaymentReceiveCreateDTO extends IPaymentReceivedCommonDTO {
|
||||
attachments?: AttachmentLinkDTO[];
|
||||
}
|
||||
|
||||
export interface IPaymentReceiveEditDTO extends IPaymentReceivedCommonDTO {
|
||||
export interface IPaymentReceiveEditDTO {
|
||||
customerId: number;
|
||||
paymentDate: Date;
|
||||
amount: number;
|
||||
@@ -189,11 +184,3 @@ export interface PaymentReceiveMailPresendEvent {
|
||||
paymentReceiveId: number;
|
||||
messageOptions: PaymentReceiveMailOptsDTO;
|
||||
}
|
||||
|
||||
export interface PaymentReceiveUnearnedRevenueAppliedEventPayload {
|
||||
tenantId: number;
|
||||
paymentReceiveId: number;
|
||||
saleInvoiceId: number;
|
||||
appliedAmount: number;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -216,9 +216,3 @@ export interface ISaleInvoiceMailSent {
|
||||
saleInvoiceId: number;
|
||||
messageOptions: SendInvoiceMailDTO;
|
||||
}
|
||||
|
||||
export interface SaleInvoiceAppliedUnearnedRevenueOnCreatedEventPayload {
|
||||
tenantId: number;
|
||||
saleInvoiceId: number;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -113,8 +113,7 @@ import { UnlinkBankRuleOnDeleteBankRule } from '@/services/Banking/Rules/events/
|
||||
import { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch';
|
||||
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
|
||||
import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize';
|
||||
import { AutoApplyUnearnedRevenueOnInvoiceCreated } from '@/services/Sales/PaymentReceives/events/AutoApplyUnearnedRevenueOnInvoiceCreated';
|
||||
import { AutoApplyPrepardExpensesOnBillCreated } from '@/services/Purchases/Bills/events/AutoApplyPrepardExpensesOnBillCreated';
|
||||
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
|
||||
|
||||
export default () => {
|
||||
return new EventPublisher();
|
||||
@@ -276,5 +275,8 @@ export const susbcribers = () => {
|
||||
|
||||
// Plaid
|
||||
RecognizeSyncedBankTranasctions,
|
||||
|
||||
// Loops
|
||||
LoopsEventsSubscriber
|
||||
];
|
||||
};
|
||||
|
||||
@@ -11,8 +11,6 @@ export default class BillPayment extends mixin(TenantModel, [
|
||||
CustomViewBaseModel,
|
||||
ModelSearchable,
|
||||
]) {
|
||||
prepardExpensesAccountId: number;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
@@ -49,14 +47,6 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -38,24 +38,4 @@ export default class ContactTransfromer extends Transformer {
|
||||
? this.formatDate(contact.openingBalanceAt)
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the unused credit balance.
|
||||
* @param {IContact} contact
|
||||
* @returns {number}
|
||||
*/
|
||||
protected unusedCredit = (contact: IContact): number => {
|
||||
return contact.balance > 0 ? 0 : Math.abs(contact.balance);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted unused credit balance.
|
||||
* @param {IContact} contact
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedUnusedCredit = (contact: IContact): string => {
|
||||
const unusedCredit = this.unusedCredit(contact);
|
||||
|
||||
return formatNumber(unusedCredit, { currencyCode: contact.currencyCode });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ export default class CustomerTransfromer extends ContactTransfromer {
|
||||
'formattedOpeningBalanceAt',
|
||||
'customerType',
|
||||
'formattedCustomerType',
|
||||
'unusedCredit',
|
||||
'formattedUnusedCredit',
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Service } from 'typedi';
|
||||
import ContactTransfromer from '../ContactTransformer';
|
||||
|
||||
export default class VendorTransfromer extends ContactTransfromer {
|
||||
@@ -9,9 +10,7 @@ export default class VendorTransfromer extends ContactTransfromer {
|
||||
return [
|
||||
'formattedBalance',
|
||||
'formattedOpeningBalance',
|
||||
'formattedOpeningBalanceAt',
|
||||
'unusedCredit',
|
||||
'formattedUnusedCredit',
|
||||
'formattedOpeningBalanceAt'
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export const DEFAULT_VIEWS = [
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const ERRORS = {
|
||||
OPENING_BALANCE_DATE_REQUIRED: 'OPENING_BALANCE_DATE_REQUIRED',
|
||||
CONTACT_ALREADY_INACTIVE: 'CONTACT_ALREADY_INACTIVE',
|
||||
|
||||
51
packages/server/src/services/Loops/LoopsEventsSubscriber.ts
Normal file
51
packages/server/src/services/Loops/LoopsEventsSubscriber.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import axios from 'axios';
|
||||
import config from '@/config';
|
||||
import { IAuthSignUpVerifiedEventPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { SystemUser } from '@/system/models';
|
||||
|
||||
export class LoopsEventsSubscriber {
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.auth.signUpConfirmed,
|
||||
this.triggerEventOnSignupVerified.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the user verified sends the event to the Loops.
|
||||
* @param {IAuthSignUpVerifiedEventPayload} param0
|
||||
*/
|
||||
public async triggerEventOnSignupVerified({
|
||||
email,
|
||||
userId,
|
||||
}: IAuthSignUpVerifiedEventPayload) {
|
||||
// Can't continue since the Loops the api key is not configured.
|
||||
if (!config.loops.apiKey) {
|
||||
return;
|
||||
}
|
||||
const user = await SystemUser.query().findById(userId);
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: 'https://app.loops.so/api/v1/events/send',
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.loops.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
email,
|
||||
userId,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
eventName: 'USER_VERIFIED',
|
||||
eventProperties: {},
|
||||
mailingLists: {},
|
||||
},
|
||||
};
|
||||
await axios(options);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
import moment from 'moment';
|
||||
import { sumBy, chain } from 'lodash';
|
||||
import { sumBy } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
AccountNormal,
|
||||
IBillPayment,
|
||||
IBillPaymentEntry,
|
||||
ILedger,
|
||||
ILedgerEntry,
|
||||
} from '@/interfaces';
|
||||
import { AccountNormal, IBillPayment, ILedgerEntry } from '@/interfaces';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
@@ -27,7 +21,6 @@ export class BillPaymentGLEntries {
|
||||
* @param {number} tenantId
|
||||
* @param {number} billPaymentId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writePaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
@@ -72,7 +65,6 @@ export class BillPaymentGLEntries {
|
||||
* @param {number} tenantId
|
||||
* @param {number} billPaymentId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public rewritePaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
@@ -110,7 +102,7 @@ export class BillPaymentGLEntries {
|
||||
* @param {IBillPayment} billPayment
|
||||
* @returns {}
|
||||
*/
|
||||
private getPaymentCommonEntry = (billPayment: IBillPayment): ILedgerEntry => {
|
||||
private getPaymentCommonEntry = (billPayment: IBillPayment) => {
|
||||
const formattedDate = moment(billPayment.paymentDate).format('YYYY-MM-DD');
|
||||
|
||||
return {
|
||||
@@ -135,7 +127,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 => {
|
||||
@@ -149,10 +141,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 = (
|
||||
@@ -194,7 +186,7 @@ export class BillPaymentGLEntries {
|
||||
|
||||
/**
|
||||
* Retrieves the payment deposit GL entry.
|
||||
* @param {IBillPayment} billPayment
|
||||
* @param {IBillPayment} billPayment
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getPaymentGLEntry = (billPayment: IBillPayment): ILedgerEntry => {
|
||||
@@ -206,7 +198,6 @@ export class BillPaymentGLEntries {
|
||||
accountId: billPayment.paymentAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
index: 2,
|
||||
indexGroup: 10,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -235,8 +226,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 = (
|
||||
@@ -263,53 +254,10 @@ 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 = (
|
||||
@@ -318,79 +266,12 @@ export class BillPaymentGLEntries {
|
||||
gainLossAccountId: number,
|
||||
baseCurrency: string
|
||||
): Ledger => {
|
||||
const entries = billPayment.isPrepardExpense
|
||||
? this.getPrepardExpenseGLEntries(APAccountId, billPayment)
|
||||
: this.getPaymentGLEntries(
|
||||
billPayment,
|
||||
APAccountId,
|
||||
gainLossAccountId,
|
||||
baseCurrency
|
||||
);
|
||||
const entries = 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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ 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
|
||||
@@ -32,8 +28,7 @@ export class PaymentWriteGLEntriesSubscriber {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles bill payment writing journal entries once created.
|
||||
* @param {IBillPaymentEventCreatedPayload} payload -
|
||||
* Handle bill payment writing journal entries once created.
|
||||
*/
|
||||
private handleWriteJournalEntries = async ({
|
||||
tenantId,
|
||||
@@ -49,22 +44,6 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -11,9 +11,6 @@ export class CommandBillPaymentDTOTransformer {
|
||||
@Inject()
|
||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Transforms create/edit DTO to model.
|
||||
* @param {number} tenantId
|
||||
@@ -27,27 +24,17 @@ export class CommandBillPaymentDTOTransformer {
|
||||
vendor: IVendor,
|
||||
oldBillPayment?: IBillPayment
|
||||
): Promise<IBillPayment> {
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
const appliedAmount = sumBy(billPaymentDTO.entries, 'paymentAmount');
|
||||
|
||||
const hasPrepardExpenses = appliedAmount < billPaymentDTO.amount;
|
||||
const prepardExpensesAccount = hasPrepardExpenses
|
||||
? await accountRepository.findOrCreatePrepardExpenses()
|
||||
: null;
|
||||
const prepardExpensesAccountId =
|
||||
hasPrepardExpenses && prepardExpensesAccount
|
||||
? billPaymentDTO.prepardExpensesAccountId ?? prepardExpensesAccount?.id
|
||||
: billPaymentDTO.prepardExpensesAccountId;
|
||||
const amount =
|
||||
billPaymentDTO.amount ?? sumBy(billPaymentDTO.entries, 'paymentAmount');
|
||||
|
||||
const initialDTO = {
|
||||
...formatDateFields(omit(billPaymentDTO, ['attachments']), [
|
||||
'paymentDate',
|
||||
]),
|
||||
appliedAmount,
|
||||
amount,
|
||||
currencyCode: vendor.currencyCode,
|
||||
exchangeRate: billPaymentDTO.exchangeRate || 1,
|
||||
entries: billPaymentDTO.entries,
|
||||
prepardExpensesAccountId,
|
||||
};
|
||||
return R.compose(
|
||||
this.branchDTOTransform.transformDTO<IBillPayment>(tenantId)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import PromisePool, { ProcessHandler } from '@supercharge/promise-pool';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import {
|
||||
IBillPayment,
|
||||
IBillPrepardExpensesAppliedEventPayload,
|
||||
IPaymentPrepardExpensesAppliedEventPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class AutoApplyPrepardExpenses {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Auto apply prepard expenses to the given bill.
|
||||
* @param {number} tenantId
|
||||
* @param {number} billId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async autoApplyPrepardExpensesToBill(
|
||||
tenantId: number,
|
||||
billId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { BillPayment, Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
const bill = await Bill.query(trx).findById(billId).throwIfNotFound();
|
||||
|
||||
const unappliedPayments = await BillPayment.query(trx)
|
||||
.where('vendorId', bill.vendorId)
|
||||
.whereRaw('amount - applied_amount > 0')
|
||||
.whereNotNull('prepardExpensesAccountId');
|
||||
|
||||
let unappliedAmount = bill.total;
|
||||
let appliedTotalAmount = 0; // Total applied amount after applying.
|
||||
|
||||
const precessHandler: ProcessHandler<IBillPayment, void> = async (
|
||||
unappliedPayment: IBillPayment,
|
||||
index: number,
|
||||
pool
|
||||
) => {
|
||||
const appliedAmount = Math.min(unappliedAmount, unappliedPayment.amount);
|
||||
unappliedAmount = unappliedAmount - appliedAmount;
|
||||
appliedTotalAmount += appliedAmount;
|
||||
|
||||
// Stop applying once the unapplied amount reach zero or less.
|
||||
if (appliedAmount <= 0) {
|
||||
pool.stop();
|
||||
return;
|
||||
}
|
||||
await this.applyBillToPaymentMade(
|
||||
tenantId,
|
||||
unappliedPayment.id,
|
||||
bill.id,
|
||||
appliedAmount,
|
||||
trx
|
||||
);
|
||||
};
|
||||
await PromisePool.withConcurrency(1)
|
||||
.for(unappliedPayments)
|
||||
.process(precessHandler);
|
||||
|
||||
// Increase the paid amount of the purchase invoice.
|
||||
await Bill.changePaymentAmount(billId, appliedTotalAmount, trx);
|
||||
|
||||
// Triggers `onBillPrepardExpensesApplied` event.
|
||||
await this.eventPublisher.emitAsync(events.bill.onPrepardExpensesApplied, {
|
||||
tenantId,
|
||||
billId,
|
||||
trx,
|
||||
} as IBillPrepardExpensesAppliedEventPayload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given bill to payment made transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} billPaymentId
|
||||
* @param {number} billId
|
||||
* @param {number} appliedAmount
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public applyBillToPaymentMade = async (
|
||||
tenantId: number,
|
||||
billPaymentId: number,
|
||||
billId: number,
|
||||
appliedAmount: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { BillPaymentEntry, BillPayment } = this.tenancy.models(tenantId);
|
||||
|
||||
await BillPaymentEntry.query(trx).insert({
|
||||
billPaymentId,
|
||||
billId,
|
||||
paymentAmount: appliedAmount,
|
||||
});
|
||||
await BillPayment.query(trx).increment('appliedAmount', appliedAmount);
|
||||
|
||||
// Triggers `onBillPaymentPrepardExpensesApplied` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.billPayment.onPrepardExpensesApplied,
|
||||
{
|
||||
tenantId,
|
||||
billPaymentId,
|
||||
billId,
|
||||
appliedAmount,
|
||||
trx,
|
||||
} as IPaymentPrepardExpensesAppliedEventPayload
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { AutoApplyPrepardExpenses } from '../AutoApplyPrepardExpenses';
|
||||
import events from '@/subscribers/events';
|
||||
import { IBillCreatedPayload } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class AutoApplyPrepardExpensesOnBillCreated {
|
||||
@Inject()
|
||||
private autoApplyPrepardExpenses: AutoApplyPrepardExpenses;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreated,
|
||||
this.handleAutoApplyPrepardExpensesOnBillCreated.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the auto apply prepard expenses on bill created.
|
||||
* @param {IBillCreatedPayload} payload -
|
||||
*/
|
||||
private async handleAutoApplyPrepardExpensesOnBillCreated({
|
||||
tenantId,
|
||||
billId,
|
||||
trx,
|
||||
}: IBillCreatedPayload) {
|
||||
await this.autoApplyPrepardExpenses.autoApplyPrepardExpensesToBill(
|
||||
tenantId,
|
||||
billId,
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import PromisePool, { ProcessHandler } from '@supercharge/promise-pool';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IPaymentReceive,
|
||||
PaymentReceiveUnearnedRevenueAppliedEventPayload,
|
||||
SaleInvoiceAppliedUnearnedRevenueOnCreatedEventPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class AutoApplyUnearnedRevenue {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Auto apply invoice to advanced payment received transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {number} invoiceId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async autoApplyUnearnedRevenueToInvoice(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
const invoice = await SaleInvoice.query(trx)
|
||||
.findById(saleInvoiceId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const unappliedPayments = await PaymentReceive.query(trx)
|
||||
.where('customerId', invoice.customerId)
|
||||
.whereRaw('amount - applied_amount > 0')
|
||||
.whereNotNull('unearnedRevenueAccountId');
|
||||
|
||||
let unappliedAmount = invoice.total;
|
||||
let appliedTotalAmount = 0; // Total applied amount after applying.
|
||||
|
||||
const processHandler: ProcessHandler<
|
||||
IPaymentReceive,
|
||||
Promise<void>
|
||||
> = async (unappliedPayment: IPaymentReceive, index: number, pool) => {
|
||||
const appliedAmount = Math.min(unappliedAmount, unappliedPayment.amount);
|
||||
unappliedAmount = unappliedAmount - appliedAmount;
|
||||
appliedTotalAmount += appliedAmount;
|
||||
|
||||
// Stop applying once the unapplied amount reache zero or less.
|
||||
if (appliedAmount <= 0) {
|
||||
pool.stop();
|
||||
return;
|
||||
}
|
||||
await this.applyInvoiceToPaymentReceived(
|
||||
tenantId,
|
||||
unappliedPayment.id,
|
||||
invoice.id,
|
||||
appliedAmount,
|
||||
trx
|
||||
);
|
||||
};
|
||||
await PromisePool.withConcurrency(1)
|
||||
.for(unappliedPayments)
|
||||
.process(processHandler);
|
||||
|
||||
// Increase the paid amount of the sale invoice.
|
||||
await SaleInvoice.changePaymentAmount(
|
||||
saleInvoiceId,
|
||||
appliedTotalAmount,
|
||||
trx
|
||||
);
|
||||
// Triggers event `onSaleInvoiceUnearnedRevenue`.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleInvoice.onUnearnedRevenueApplied,
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
trx,
|
||||
} as SaleInvoiceAppliedUnearnedRevenueOnCreatedEventPayload
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given invoice to payment received transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceivedId
|
||||
* @param {number} invoiceId
|
||||
* @param {number} appliedAmount
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public applyInvoiceToPaymentReceived = async (
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
invoiceId: number,
|
||||
appliedAmount: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const { PaymentReceiveEntry, PaymentReceive } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
await PaymentReceiveEntry.query(trx).insert({
|
||||
paymentReceiveId,
|
||||
invoiceId,
|
||||
paymentAmount: appliedAmount,
|
||||
});
|
||||
await PaymentReceive.query(trx).increment('appliedAmount', appliedAmount);
|
||||
|
||||
// Triggers the event `onPaymentReceivedUnearnedRevenue`.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.paymentReceive.onUnearnedRevenueApplied,
|
||||
{
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
saleInvoiceId: invoiceId,
|
||||
appliedAmount,
|
||||
trx,
|
||||
} as PaymentReceiveUnearnedRevenueAppliedEventPayload
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import { PaymentReceiveValidators } from './PaymentReceiveValidators';
|
||||
import { PaymentReceiveIncrement } from './PaymentReceiveIncrement';
|
||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||
import { formatDateFields } from '@/utils';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class PaymentReceiveDTOTransformer {
|
||||
@@ -24,9 +23,6 @@ export class PaymentReceiveDTOTransformer {
|
||||
@Inject()
|
||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Transformes the create payment receive DTO to model object.
|
||||
* @param {number} tenantId
|
||||
@@ -40,8 +36,9 @@ export class PaymentReceiveDTOTransformer {
|
||||
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
|
||||
oldPaymentReceive?: IPaymentReceive
|
||||
): Promise<IPaymentReceive> {
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
const appliedAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||
const amount =
|
||||
paymentReceiveDTO.amount ??
|
||||
sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber =
|
||||
@@ -55,29 +52,17 @@ export class PaymentReceiveDTOTransformer {
|
||||
|
||||
this.validators.validatePaymentNoRequire(paymentReceiveNo);
|
||||
|
||||
const hasUnearnedPayment = appliedAmount < paymentReceiveDTO.amount;
|
||||
const unearnedRevenueAccount = hasUnearnedPayment
|
||||
? await accountRepository.findOrCreateUnearnedRevenue()
|
||||
: null;
|
||||
|
||||
const unearnedRevenueAccountId =
|
||||
hasUnearnedPayment && unearnedRevenueAccount
|
||||
? paymentReceiveDTO.unearnedRevenueAccountId ??
|
||||
unearnedRevenueAccount?.id
|
||||
: paymentReceiveDTO.unearnedRevenueAccountId;
|
||||
|
||||
const initialDTO = {
|
||||
...formatDateFields(omit(paymentReceiveDTO, ['entries', 'attachments']), [
|
||||
'paymentDate',
|
||||
]),
|
||||
appliedAmount,
|
||||
amount,
|
||||
currencyCode: customer.currencyCode,
|
||||
...(paymentReceiveNo ? { paymentReceiveNo } : {}),
|
||||
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
|
||||
entries: paymentReceiveDTO.entries.map((entry) => ({
|
||||
...entry,
|
||||
})),
|
||||
unearnedRevenueAccountId,
|
||||
};
|
||||
return R.compose(
|
||||
this.branchDTOTransform.transformDTO<IPaymentReceive>(tenantId)
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { sumBy } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { IPaymentReceive, ILedgerEntry, AccountNormal } from '@/interfaces';
|
||||
import {
|
||||
IPaymentReceive,
|
||||
ILedgerEntry,
|
||||
AccountNormal,
|
||||
IPaymentReceiveGLCommonEntry,
|
||||
} from '@/interfaces';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
import { PaymentReceivedGLCommon } from './PaymentReceivedGLCommon';
|
||||
|
||||
@Service()
|
||||
export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
export class PaymentReceiveGLEntries {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@@ -17,9 +22,9 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
|
||||
/**
|
||||
* Writes payment GL entries to the storage.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} paymentReceiveId - Payment received id.
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writePaymentGLEntries = async (
|
||||
@@ -29,19 +34,14 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
): Promise<void> => {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the given tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
// Retrieves the payment receive with associated entries.
|
||||
const paymentReceive = await PaymentReceive.query(trx)
|
||||
.findById(paymentReceiveId)
|
||||
.withGraphFetched('entries.invoice');
|
||||
|
||||
// Cannot continue if the received payment is unearned revenue type,
|
||||
// that type of transactions have different type of GL entries.
|
||||
if (paymentReceive.unearnedRevenueAccountId) {
|
||||
return;
|
||||
}
|
||||
// Retrieves the given tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
// Retrives the payment receive ledger.
|
||||
const ledger = await this.getPaymentReceiveGLedger(
|
||||
tenantId,
|
||||
@@ -53,6 +53,25 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts the given payment receive GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public revertPaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
await this.ledgerStorage.deleteByReference(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
'PaymentReceive',
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrites the given payment receive GL entries.
|
||||
* @param {number} tenantId
|
||||
@@ -73,10 +92,10 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
|
||||
/**
|
||||
* Retrieves the payment receive general ledger.
|
||||
* @param {number} tenantId -
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @param {string} baseCurrencyCode -
|
||||
* @param {Knex.Transaction} trx -
|
||||
* @param {number} tenantId -
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @param {string} baseCurrencyCode -
|
||||
* @param {Knex.Transaction} trx -
|
||||
* @returns {Ledger}
|
||||
*/
|
||||
public getPaymentReceiveGLedger = async (
|
||||
@@ -107,9 +126,100 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
return new Ledger(ledgerEntries);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the payment total exchange gain/loss.
|
||||
* @param {IBillPayment} paymentReceive - Payment receive with entries.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPaymentExGainOrLoss = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): number => {
|
||||
return sumBy(paymentReceive.entries, (entry) => {
|
||||
const paymentLocalAmount =
|
||||
entry.paymentAmount * paymentReceive.exchangeRate;
|
||||
const invoicePayment = entry.paymentAmount * entry.invoice.exchangeRate;
|
||||
|
||||
return paymentLocalAmount - invoicePayment;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the common entry of payment receive.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @returns {}
|
||||
*/
|
||||
private getPaymentReceiveCommonEntry = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): IPaymentReceiveGLCommonEntry => {
|
||||
return {
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
|
||||
currencyCode: paymentReceive.currencyCode,
|
||||
exchangeRate: paymentReceive.exchangeRate,
|
||||
|
||||
transactionId: paymentReceive.id,
|
||||
transactionType: 'PaymentReceive',
|
||||
|
||||
transactionNumber: paymentReceive.paymentReceiveNo,
|
||||
referenceNumber: paymentReceive.referenceNo,
|
||||
|
||||
date: paymentReceive.paymentDate,
|
||||
userId: paymentReceive.userId,
|
||||
createdAt: paymentReceive.createdAt,
|
||||
|
||||
branchId: paymentReceive.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the payment exchange gain/loss entry.
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @param {number} ARAccountId -
|
||||
* @param {number} exchangeGainOrLossAccountId -
|
||||
* @param {string} baseCurrencyCode -
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
private getPaymentExchangeGainLossEntry = (
|
||||
paymentReceive: IPaymentReceive,
|
||||
ARAccountId: number,
|
||||
exchangeGainOrLossAccountId: number,
|
||||
baseCurrencyCode: string
|
||||
): ILedgerEntry[] => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
|
||||
const gainOrLoss = this.getPaymentExGainOrLoss(paymentReceive);
|
||||
const absGainOrLoss = Math.abs(gainOrLoss);
|
||||
|
||||
return gainOrLoss
|
||||
? [
|
||||
{
|
||||
...commonJournal,
|
||||
currencyCode: baseCurrencyCode,
|
||||
exchangeRate: 1,
|
||||
debit: gainOrLoss > 0 ? absGainOrLoss : 0,
|
||||
credit: gainOrLoss < 0 ? absGainOrLoss : 0,
|
||||
accountId: ARAccountId,
|
||||
contactId: paymentReceive.customerId,
|
||||
index: 3,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
},
|
||||
{
|
||||
...commonJournal,
|
||||
currencyCode: baseCurrencyCode,
|
||||
exchangeRate: 1,
|
||||
credit: gainOrLoss > 0 ? absGainOrLoss : 0,
|
||||
debit: gainOrLoss < 0 ? absGainOrLoss : 0,
|
||||
accountId: exchangeGainOrLossAccountId,
|
||||
index: 3,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the payment deposit GL entry.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getPaymentDepositGLEntry = (
|
||||
@@ -128,8 +238,8 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
|
||||
/**
|
||||
* Retrieves the payment receivable entry.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {number} ARAccountId
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {number} ARAccountId
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getPaymentReceivableEntry = (
|
||||
@@ -152,15 +262,15 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
||||
* Records payment receive journal transactions.
|
||||
*
|
||||
* Invoice payment journals.
|
||||
* ------------
|
||||
* - Account Receivable -> Debit
|
||||
* - Payment Account [current asset] -> Credit
|
||||
* --------
|
||||
* - Account receivable -> Debit
|
||||
* - Payment account [current asset] -> Credit
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {IPaymentReceive} paymentRecieve - Payment receive model.
|
||||
* @param {number} ARAccountId - A/R account id.
|
||||
* @param {number} exGainOrLossAccountId - Exchange gain/loss account id.
|
||||
* @param {string} baseCurrency - Base currency code.
|
||||
* @param {number} tenantId
|
||||
* @param {IPaymentReceive} paymentRecieve - Payment receive model.
|
||||
* @param {number} ARAccountId - A/R account id.
|
||||
* @param {number} exGainOrLossAccountId - Exchange gain/loss account id.
|
||||
* @param {string} baseCurrency - Base currency code.
|
||||
* @returns {Promise<ILedgerEntry>}
|
||||
*/
|
||||
public getPaymentReceiveGLEntries = (
|
||||
|
||||
@@ -107,6 +107,7 @@ export class PaymentReceiveValidators {
|
||||
const invoicesIds = paymentReceiveEntries.map(
|
||||
(e: IPaymentReceiveEntryDTO) => e.invoiceId
|
||||
);
|
||||
|
||||
const storedInvoices = await SaleInvoice.query().whereIn('id', invoicesIds);
|
||||
|
||||
const storedInvoicesMap = new Map(
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import { Knex } from 'knex';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
AccountNormal,
|
||||
ILedgerEntry,
|
||||
IPaymentReceive,
|
||||
IPaymentReceiveGLCommonEntry,
|
||||
} from '@/interfaces';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
|
||||
export class PaymentReceivedGLCommon {
|
||||
private ledgerStorage: LedgerStorageService;
|
||||
|
||||
/**
|
||||
* Retrieves the common entry of payment receive.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @returns {IPaymentReceiveGLCommonEntry}
|
||||
*/
|
||||
protected getPaymentReceiveCommonEntry = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): IPaymentReceiveGLCommonEntry => {
|
||||
return {
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
|
||||
currencyCode: paymentReceive.currencyCode,
|
||||
exchangeRate: paymentReceive.exchangeRate,
|
||||
|
||||
transactionId: paymentReceive.id,
|
||||
transactionType: 'PaymentReceive',
|
||||
|
||||
transactionNumber: paymentReceive.paymentReceiveNo,
|
||||
referenceNumber: paymentReceive.referenceNo,
|
||||
|
||||
date: paymentReceive.paymentDate,
|
||||
userId: paymentReceive.userId,
|
||||
createdAt: paymentReceive.createdAt,
|
||||
|
||||
branchId: paymentReceive.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the payment exchange gain/loss entry.
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @param {number} ARAccountId -
|
||||
* @param {number} exchangeGainOrLossAccountId -
|
||||
* @param {string} baseCurrencyCode -
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
protected getPaymentExchangeGainLossEntry = (
|
||||
paymentReceive: IPaymentReceive,
|
||||
ARAccountId: number,
|
||||
exchangeGainOrLossAccountId: number,
|
||||
baseCurrencyCode: string
|
||||
): ILedgerEntry[] => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
|
||||
const gainOrLoss = this.getPaymentExGainOrLoss(paymentReceive);
|
||||
const absGainOrLoss = Math.abs(gainOrLoss);
|
||||
|
||||
return gainOrLoss
|
||||
? [
|
||||
{
|
||||
...commonJournal,
|
||||
currencyCode: baseCurrencyCode,
|
||||
exchangeRate: 1,
|
||||
debit: gainOrLoss > 0 ? absGainOrLoss : 0,
|
||||
credit: gainOrLoss < 0 ? absGainOrLoss : 0,
|
||||
accountId: ARAccountId,
|
||||
contactId: paymentReceive.customerId,
|
||||
index: 3,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
},
|
||||
{
|
||||
...commonJournal,
|
||||
currencyCode: baseCurrencyCode,
|
||||
exchangeRate: 1,
|
||||
credit: gainOrLoss > 0 ? absGainOrLoss : 0,
|
||||
debit: gainOrLoss < 0 ? absGainOrLoss : 0,
|
||||
accountId: exchangeGainOrLossAccountId,
|
||||
index: 3,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the payment total exchange gain/loss.
|
||||
* @param {IBillPayment} paymentReceive - Payment receive with entries.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPaymentExGainOrLoss = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): number => {
|
||||
return sumBy(paymentReceive.entries, (entry) => {
|
||||
const paymentLocalAmount =
|
||||
entry.paymentAmount * paymentReceive.exchangeRate;
|
||||
const invoicePayment = entry.paymentAmount * entry.invoice.exchangeRate;
|
||||
|
||||
return paymentLocalAmount - invoicePayment;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts the given payment receive GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public revertPaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
await this.ledgerStorage.deleteByReference(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
'PaymentReceive',
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { flatten } from 'lodash';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
import { PaymentReceivedGLCommon } from './PaymentReceivedGLCommon';
|
||||
import {
|
||||
AccountNormal,
|
||||
ILedgerEntry,
|
||||
IPaymentReceive,
|
||||
IPaymentReceiveEntry,
|
||||
} from '@/interfaces';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
|
||||
@Service()
|
||||
export class PaymentReceivedUnearnedGLEntries extends PaymentReceivedGLCommon {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private ledgerStorage: LedgerStorageService;
|
||||
|
||||
/**
|
||||
* Writes payment GL entries to the storage.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writePaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the payment receive with associated entries.
|
||||
const paymentReceive = await PaymentReceive.query(trx)
|
||||
.findById(paymentReceiveId)
|
||||
.withGraphFetched('entries.invoice');
|
||||
|
||||
// Stop early if
|
||||
if (!paymentReceive.unearnedRevenueAccountId) {
|
||||
return;
|
||||
}
|
||||
// Retrieves the given tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
const ledger = await this.getPaymentReceiveGLedger(
|
||||
tenantId,
|
||||
paymentReceive
|
||||
);
|
||||
// Commit the ledger entries to the storage.
|
||||
await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrites the given payment receive GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public rewritePaymentGLEntries = async (
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
// Reverts the payment GL entries.
|
||||
await this.revertPaymentGLEntries(tenantId, paymentReceiveId, trx);
|
||||
|
||||
// Writes the payment GL entries.
|
||||
await this.writePaymentGLEntries(tenantId, paymentReceiveId, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the payment received GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @returns {Promise<Ledger>}
|
||||
*/
|
||||
private getPaymentReceiveGLedger = async (
|
||||
tenantId: number,
|
||||
paymentReceive: IPaymentReceive
|
||||
) => {
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve the A/R account of the given currency.
|
||||
const receivableAccount =
|
||||
await accountRepository.findOrCreateAccountReceivable(
|
||||
paymentReceive.currencyCode
|
||||
);
|
||||
// Retrieve the payment GL entries.
|
||||
const entries = this.getPaymentGLEntries(
|
||||
receivableAccount.id,
|
||||
paymentReceive
|
||||
);
|
||||
const unearnedRevenueEntries =
|
||||
this.getUnearnedRevenueEntries(paymentReceive);
|
||||
|
||||
const combinedEntries = [...unearnedRevenueEntries, ...entries];
|
||||
|
||||
return new Ledger(combinedEntries);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the payment received GL entries.
|
||||
* @param {number} ARAccountId - A/R account id.
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @returns {Array<ILedgerEntry>}
|
||||
*/
|
||||
private getPaymentGLEntries = R.curry(
|
||||
(
|
||||
ARAccountId: number,
|
||||
paymentReceive: IPaymentReceive
|
||||
): Array<ILedgerEntry> => {
|
||||
const getPaymentEntryGLEntries = this.getPaymentEntryGLEntries(
|
||||
ARAccountId,
|
||||
paymentReceive
|
||||
);
|
||||
const entriesGroup = paymentReceive.entries.map((paymentEntry) => {
|
||||
return getPaymentEntryGLEntries(paymentEntry);
|
||||
});
|
||||
return flatten(entriesGroup);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve the payment entry GL entries.
|
||||
* @param {IPaymentReceiveEntry} paymentReceivedEntry -
|
||||
* @param {IPaymentReceive} paymentReceive -
|
||||
* @returns {Array<ILedgerEntry>}
|
||||
*/
|
||||
private getPaymentEntryGLEntries = R.curry(
|
||||
(
|
||||
ARAccountId: number,
|
||||
paymentReceive: IPaymentReceive,
|
||||
paymentReceivedEntry: IPaymentReceiveEntry
|
||||
): Array<ILedgerEntry> => {
|
||||
const unearnedRevenueEntry = this.getDebitUnearnedRevenueGLEntry(
|
||||
paymentReceivedEntry.paymentAmount,
|
||||
paymentReceive
|
||||
);
|
||||
const AREntry = this.getPaymentReceivableEntry(
|
||||
paymentReceivedEntry.paymentAmount,
|
||||
paymentReceive,
|
||||
ARAccountId
|
||||
);
|
||||
return [unearnedRevenueEntry, AREntry];
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the payment deposit GL entry.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getDebitUnearnedRevenueGLEntry = (
|
||||
amount: number,
|
||||
paymentReceive: IPaymentReceive
|
||||
): ILedgerEntry => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
|
||||
|
||||
return {
|
||||
...commonJournal,
|
||||
debit: amount,
|
||||
accountId: paymentReceive.unearnedRevenueAccountId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
index: 2,
|
||||
indexGroup: 20,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the payment receivable entry.
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {number} ARAccountId
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getPaymentReceivableEntry = (
|
||||
amount: number,
|
||||
paymentReceive: IPaymentReceive,
|
||||
ARAccountId: number
|
||||
): ILedgerEntry => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
|
||||
|
||||
return {
|
||||
...commonJournal,
|
||||
credit: amount,
|
||||
contactId: paymentReceive.customerId,
|
||||
accountId: ARAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
index: 1,
|
||||
indexGroup: 20,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the unearned revenue entries.
|
||||
* @param {IPaymentReceive} paymentReceived -
|
||||
* @returns {Array<ILedgerEntry>}
|
||||
*/
|
||||
private getUnearnedRevenueEntries = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): Array<ILedgerEntry> => {
|
||||
const depositEntry = this.getDepositPaymentGLEntry(paymentReceive);
|
||||
const unearnedEntry = this.getUnearnedRevenueEntry(paymentReceive);
|
||||
|
||||
return [depositEntry, unearnedEntry];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the payment deposit entry.
|
||||
* @param {IPaymentReceive} paymentReceived -
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getDepositPaymentGLEntry = (
|
||||
paymentReceive: IPaymentReceive
|
||||
): ILedgerEntry => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
|
||||
|
||||
return {
|
||||
...commonJournal,
|
||||
debit: paymentReceive.amount,
|
||||
accountId: paymentReceive.depositAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
indexGroup: 10,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the unearned revenue entry.
|
||||
* @param {IPaymentReceive} paymentReceived -
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getUnearnedRevenueEntry = (
|
||||
paymentReceived: IPaymentReceive
|
||||
): ILedgerEntry => {
|
||||
const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceived);
|
||||
|
||||
return {
|
||||
...commonJournal,
|
||||
credit: paymentReceived.amount,
|
||||
accountId: paymentReceived.unearnedRevenueAccountId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
indexGroup: 10,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { AutoApplyUnearnedRevenue } from '../AutoApplyUnearnedRevenue';
|
||||
|
||||
@Service()
|
||||
export class AutoApplyUnearnedRevenueOnInvoiceCreated {
|
||||
@Inject()
|
||||
private autoApplyUnearnedRevenue: AutoApplyUnearnedRevenue;
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onCreated,
|
||||
this.handleAutoApplyUnearnedRevenueOnInvoiceCreated.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the auto apply unearned revenue on invoice creating.
|
||||
* @param
|
||||
*/
|
||||
private async handleAutoApplyUnearnedRevenueOnInvoiceCreated({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
trx,
|
||||
}) {
|
||||
await this.autoApplyUnearnedRevenue.autoApplyUnearnedRevenueToInvoice(
|
||||
tenantId,
|
||||
saleInvoice.id,
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,15 @@ import {
|
||||
IPaymentReceiveCreatedPayload,
|
||||
IPaymentReceiveDeletedPayload,
|
||||
IPaymentReceiveEditedPayload,
|
||||
PaymentReceiveUnearnedRevenueAppliedEventPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { PaymentReceiveGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceiveGLEntries';
|
||||
import { PaymentReceivedUnearnedGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceivedUnearnedGLEntries';
|
||||
|
||||
@Service()
|
||||
export default class PaymentReceivesWriteGLEntriesSubscriber {
|
||||
@Inject()
|
||||
private paymentReceiveGLEntries: PaymentReceiveGLEntries;
|
||||
|
||||
@Inject()
|
||||
private paymentReceivedUnearnedGLEntries: PaymentReceivedUnearnedGLEntries;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
@@ -37,7 +32,6 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
||||
|
||||
/**
|
||||
* Handle journal entries writing once the payment receive created.
|
||||
* @param {IPaymentReceiveCreatedPayload} payload -
|
||||
*/
|
||||
private handleWriteJournalEntriesOnceCreated = async ({
|
||||
tenantId,
|
||||
@@ -49,21 +43,14 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
||||
paymentReceiveId,
|
||||
trx
|
||||
);
|
||||
await this.paymentReceivedUnearnedGLEntries.writePaymentGLEntries(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle journal entries writing once the payment receive edited.
|
||||
* @param {IPaymentReceiveEditedPayload} payload -
|
||||
*/
|
||||
private handleOverwriteJournalEntriesOnceEdited = async ({
|
||||
tenantId,
|
||||
paymentReceive,
|
||||
paymentReceiveId,
|
||||
trx,
|
||||
}: IPaymentReceiveEditedPayload) => {
|
||||
await this.paymentReceiveGLEntries.rewritePaymentGLEntries(
|
||||
@@ -71,16 +58,10 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
||||
paymentReceive.id,
|
||||
trx
|
||||
);
|
||||
await this.paymentReceivedUnearnedGLEntries.rewritePaymentGLEntries(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles revert journal entries once deleted.
|
||||
* @param {IPaymentReceiveDeletedPayload} payload -
|
||||
*/
|
||||
private handleRevertJournalEntriesOnceDeleted = async ({
|
||||
tenantId,
|
||||
|
||||
@@ -40,6 +40,13 @@ export default {
|
||||
baseCurrencyUpdated: 'onOrganizationBaseCurrencyUpdated',
|
||||
},
|
||||
|
||||
/**
|
||||
* User subscription events.
|
||||
*/
|
||||
subscription: {
|
||||
onSubscribed: 'onOrganizationSubscribed',
|
||||
},
|
||||
|
||||
/**
|
||||
* Tenants managment service.
|
||||
*/
|
||||
@@ -142,8 +149,6 @@ export default {
|
||||
|
||||
onMailReminderSend: 'onSaleInvoiceMailReminderSend',
|
||||
onMailReminderSent: 'onSaleInvoiceMailReminderSent',
|
||||
|
||||
onUnearnedRevenueApplied: 'onSaleInvoiceUnearnedRevenue',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -232,8 +237,6 @@ export default {
|
||||
onPreMailSend: 'onPaymentReceivePreMailSend',
|
||||
onMailSend: 'onPaymentReceiveMailSend',
|
||||
onMailSent: 'onPaymentReceiveMailSent',
|
||||
|
||||
onUnearnedRevenueApplied: 'onPaymentReceivedUnearnedRevenue',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -254,8 +257,6 @@ export default {
|
||||
|
||||
onOpening: 'onBillOpening',
|
||||
onOpened: 'onBillOpened',
|
||||
|
||||
onPrepardExpensesApplied: 'onBillPrepardExpensesApplied'
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -273,8 +274,6 @@ export default {
|
||||
|
||||
onPublishing: 'onBillPaymentPublishing',
|
||||
onPublished: 'onBillPaymentPublished',
|
||||
|
||||
onPrepardExpensesApplied: 'onBillPaymentPrepardExpensesApplied'
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -160,13 +160,6 @@ export function useCustomersTableColumns() {
|
||||
width: 85,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'credit_balance',
|
||||
Header: 'Credit Balance',
|
||||
accessor: 'formatted_unused_credit',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
Header: intl.get('receivable_balance'),
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
// @ts-nocheck
|
||||
import * as R from 'ramda';
|
||||
import * as Yup from 'yup';
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { Button, Classes, Intent } from '@blueprintjs/core';
|
||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||
import { AccountsSelect, FFormGroup, FormatNumber } from '@/components';
|
||||
import { FormatNumber } from '@/components';
|
||||
import { usePaymentMadeFormContext } from '../../PaymentMadeFormProvider';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import { ACCOUNT_TYPE } from '@/constants';
|
||||
import { usePaymentMadeExcessAmount } from '../../utils';
|
||||
|
||||
interface ExcessPaymentValues {
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
accountId: '',
|
||||
} as ExcessPaymentValues;
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
accountId: Yup.number().required(),
|
||||
});
|
||||
|
||||
const DEFAULT_ACCOUNT_SLUG = 'prepaid-expenses';
|
||||
|
||||
interface ExcessPaymentValues {}
|
||||
function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
const {
|
||||
setFieldValue,
|
||||
submitForm,
|
||||
values: { currency_code: currencyCode },
|
||||
} = useFormikContext();
|
||||
@@ -37,7 +21,6 @@ function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
values: ExcessPaymentValues,
|
||||
{ setSubmitting }: FormikHelpers<ExcessPaymentValues>,
|
||||
) => {
|
||||
setFieldValue(values.accountId);
|
||||
setSubmitting(true);
|
||||
setIsExcessConfirmed(true);
|
||||
|
||||
@@ -50,20 +33,10 @@ function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
const handleCloseBtn = () => {
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
// Retrieves the default excess account id.
|
||||
const defaultAccountId = useDefaultExcessPaymentDeposit();
|
||||
|
||||
const excessAmount = usePaymentMadeExcessAmount();
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
...initialValues,
|
||||
accountId: defaultAccountId,
|
||||
}}
|
||||
validationSchema={Schema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Formik initialValues={{}} onSubmit={handleSubmit}>
|
||||
<Form>
|
||||
<ExcessPaymentDialogContentForm
|
||||
excessAmount={
|
||||
@@ -90,7 +63,6 @@ function ExcessPaymentDialogContentForm({
|
||||
onClose,
|
||||
}: ExcessPaymentDialogContentFormProps) {
|
||||
const { submitForm, isSubmitting } = useFormikContext();
|
||||
const { accounts } = usePaymentMadeFormContext();
|
||||
|
||||
const handleCloseBtn = () => {
|
||||
onClose && onClose();
|
||||
@@ -100,26 +72,8 @@ function ExcessPaymentDialogContentForm({
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<p style={{ marginBottom: 20 }}>
|
||||
Would you like to record the excess amount of{' '}
|
||||
<strong>{excessAmount}</strong> as advanced payment from the customer.
|
||||
<strong>{excessAmount}</strong> as credit payment from the vendor.
|
||||
</p>
|
||||
|
||||
<FFormGroup
|
||||
name={'accountId'}
|
||||
label={'The excessed amount will be deposited in the'}
|
||||
helperText={
|
||||
'Only other current asset and non current asset accounts will show.'
|
||||
}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'accountId'}
|
||||
items={accounts}
|
||||
buttonProps={{ small: true }}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
ACCOUNT_TYPE.NON_CURRENT_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
@@ -129,7 +83,7 @@ function ExcessPaymentDialogContentForm({
|
||||
loading={isSubmitting}
|
||||
onClick={() => submitForm()}
|
||||
>
|
||||
Save Payment
|
||||
Save Payment as Credit
|
||||
</Button>
|
||||
<Button onClick={handleCloseBtn}>Cancel</Button>
|
||||
</div>
|
||||
@@ -137,11 +91,3 @@ function ExcessPaymentDialogContentForm({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const useDefaultExcessPaymentDeposit = () => {
|
||||
const { accounts } = usePaymentMadeFormContext();
|
||||
return useMemo(() => {
|
||||
return accounts?.find((a) => a.slug === DEFAULT_ACCOUNT_SLUG)?.id;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -1,37 +1,21 @@
|
||||
// @ts-nocheck
|
||||
import { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import * as R from 'ramda';
|
||||
import { Button, Classes, Intent } from '@blueprintjs/core';
|
||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||
import { AccountsSelect, FFormGroup, FormatNumber } from '@/components';
|
||||
import { FormatNumber } from '@/components';
|
||||
import { usePaymentReceiveFormContext } from '../../PaymentReceiveFormProvider';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import { usePaymentReceivedTotalExceededAmount } from '../../utils';
|
||||
import { ACCOUNT_TYPE } from '@/constants';
|
||||
|
||||
interface ExcessPaymentValues {
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
accountId: '',
|
||||
} as ExcessPaymentValues;
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
accountId: Yup.number().required(),
|
||||
});
|
||||
|
||||
const DEFAULT_ACCOUNT_SLUG = 'unearned-revenue';
|
||||
interface ExcessPaymentValues {}
|
||||
|
||||
export function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
const {
|
||||
setFieldValue,
|
||||
submitForm,
|
||||
values: { currency_code: currencyCode },
|
||||
} = useFormikContext();
|
||||
const { setIsExcessConfirmed } = usePaymentReceiveFormContext();
|
||||
const initialAccountId = useDefaultExcessPaymentDeposit();
|
||||
const exceededAmount = usePaymentReceivedTotalExceededAmount();
|
||||
|
||||
const handleSubmit = (
|
||||
@@ -40,7 +24,6 @@ export function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
) => {
|
||||
setSubmitting(true);
|
||||
setIsExcessConfirmed(true);
|
||||
setFieldValue('unearned_revenue_account_id', values.accountId);
|
||||
|
||||
submitForm().then(() => {
|
||||
closeDialog(dialogName);
|
||||
@@ -52,14 +35,7 @@ export function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
...initialValues,
|
||||
accountId: initialAccountId,
|
||||
}}
|
||||
validationSchema={Schema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Formik initialValues={{}} onSubmit={handleSubmit}>
|
||||
<Form>
|
||||
<ExcessPaymentDialogContentForm
|
||||
exceededAmount={
|
||||
@@ -77,7 +53,6 @@ export const ExcessPaymentDialogContent = R.compose(withDialogActions)(
|
||||
);
|
||||
|
||||
function ExcessPaymentDialogContentForm({ onClose, exceededAmount }) {
|
||||
const { accounts } = usePaymentReceiveFormContext();
|
||||
const { submitForm, isSubmitting } = useFormikContext();
|
||||
|
||||
const handleCloseBtn = () => {
|
||||
@@ -89,27 +64,8 @@ function ExcessPaymentDialogContentForm({ onClose, exceededAmount }) {
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<p style={{ marginBottom: 20 }}>
|
||||
Would you like to record the excess amount of{' '}
|
||||
<strong>{exceededAmount}</strong> as advanced payment from the
|
||||
customer.
|
||||
<strong>{exceededAmount}</strong> as credit payment from the customer.
|
||||
</p>
|
||||
|
||||
<FFormGroup
|
||||
name={'accountId'}
|
||||
label={'The excessed amount will be deposited in the'}
|
||||
helperText={
|
||||
'Only other other current liability and non current liability accounts will show.'
|
||||
}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'accountId'}
|
||||
items={accounts}
|
||||
buttonProps={{ small: true }}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_LIABILITY,
|
||||
ACCOUNT_TYPE.NON_CURRENT_LIABILITY,
|
||||
]}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
@@ -120,7 +76,7 @@ function ExcessPaymentDialogContentForm({ onClose, exceededAmount }) {
|
||||
disabled={isSubmitting}
|
||||
onClick={() => submitForm()}
|
||||
>
|
||||
Save Payment
|
||||
Save Payment as Credit
|
||||
</Button>
|
||||
<Button onClick={handleCloseBtn}>Cancel</Button>
|
||||
</div>
|
||||
@@ -128,11 +84,3 @@ function ExcessPaymentDialogContentForm({ onClose, exceededAmount }) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const useDefaultExcessPaymentDeposit = () => {
|
||||
const { accounts } = usePaymentReceiveFormContext();
|
||||
return useMemo(() => {
|
||||
return accounts?.find((a) => a.slug === DEFAULT_ACCOUNT_SLUG)?.id;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -48,7 +48,6 @@ export const defaultPaymentReceive = {
|
||||
exchange_rate: 1,
|
||||
entries: [],
|
||||
attachments: [],
|
||||
unearned_revenue_account_id: '',
|
||||
};
|
||||
|
||||
export const defaultRequestPaymentEntry = {
|
||||
@@ -299,10 +298,9 @@ export const resetFormState = ({ initialValues, values, resetForm }) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const getExceededAmountFromValues = (values) => {
|
||||
const totalApplied = sumBy(values.entries, 'payment_amount');
|
||||
const totalAmount = values.amount;
|
||||
|
||||
return totalAmount - totalApplied;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,13 +183,6 @@ export function useVendorsTableColumns() {
|
||||
width: 85,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'credit_balance',
|
||||
Header: 'Credit Balance',
|
||||
accessor: 'formatted_unused_credit',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
Header: intl.get('receivable_balance'),
|
||||
|
||||
Reference in New Issue
Block a user