mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
Compare commits
1 Commits
advanced-p
...
v0.18.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b6b73b77c |
@@ -2,14 +2,6 @@
|
|||||||
|
|
||||||
All notable changes to Bigcapital server-side will be in this file.
|
All notable changes to Bigcapital server-side will be in this file.
|
||||||
|
|
||||||
## [v0.18.0] - 10-08-2024
|
|
||||||
|
|
||||||
* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
|
|
||||||
* feat: Categorize & match bank transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
|
|
||||||
* feat: Reconcile match transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/522
|
|
||||||
* fix: Issues in matching transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/523
|
|
||||||
* fix: Cashflow transactions types by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/524
|
|
||||||
|
|
||||||
## [v0.17.5] - 17-06-2024
|
## [v0.17.5] - 17-06-2024
|
||||||
|
|
||||||
* fix: Balance sheet and P/L nested accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/501
|
* fix: Balance sheet and P/L nested accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/501
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ export default class BillsPayments extends BaseController {
|
|||||||
check('vendor_id').exists().isNumeric().toInt(),
|
check('vendor_id').exists().isNumeric().toInt(),
|
||||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||||
|
|
||||||
check('amount').exists().isNumeric().toFloat(),
|
|
||||||
check('payment_account_id').exists().isNumeric().toInt(),
|
check('payment_account_id').exists().isNumeric().toInt(),
|
||||||
check('payment_number').optional({ nullable: true }).trim().escape(),
|
check('payment_number').optional({ nullable: true }).trim().escape(),
|
||||||
check('payment_date').exists(),
|
check('payment_date').exists(),
|
||||||
@@ -119,15 +118,13 @@ export default class BillsPayments extends BaseController {
|
|||||||
check('reference').optional().trim().escape(),
|
check('reference').optional().trim().escape(),
|
||||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
|
|
||||||
check('entries').exists().isArray(),
|
check('entries').exists().isArray({ min: 1 }),
|
||||||
check('entries.*.index').optional().isNumeric().toInt(),
|
check('entries.*.index').optional().isNumeric().toInt(),
|
||||||
check('entries.*.bill_id').exists().isNumeric().toInt(),
|
check('entries.*.bill_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.payment_amount').exists().isNumeric().toFloat(),
|
check('entries.*.payment_amount').exists().isNumeric().toFloat(),
|
||||||
|
|
||||||
check('attachments').isArray().optional(),
|
check('attachments').isArray().optional(),
|
||||||
check('attachments.*.key').exists().isString(),
|
check('attachments.*.key').exists().isString(),
|
||||||
|
|
||||||
check('prepard_expenses_account_id').optional().isNumeric().toInt(),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,8 +151,6 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||||
|
|
||||||
check('payment_date').exists(),
|
check('payment_date').exists(),
|
||||||
check('amount').exists().isNumeric().toFloat(),
|
|
||||||
|
|
||||||
check('reference_no').optional(),
|
check('reference_no').optional(),
|
||||||
check('deposit_account_id').exists().isNumeric().toInt(),
|
check('deposit_account_id').exists().isNumeric().toInt(),
|
||||||
check('payment_receive_no').optional({ nullable: true }).trim().escape(),
|
check('payment_receive_no').optional({ nullable: true }).trim().escape(),
|
||||||
@@ -160,7 +158,7 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
|
|
||||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
|
|
||||||
check('entries').isArray(),
|
check('entries').isArray({ min: 1 }),
|
||||||
|
|
||||||
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
check('entries.*.index').optional().isNumeric().toInt(),
|
check('entries.*.index').optional().isNumeric().toInt(),
|
||||||
@@ -169,11 +167,6 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
|
|
||||||
check('attachments').isArray().optional(),
|
check('attachments').isArray().optional(),
|
||||||
check('attachments.*.key').exists().isString(),
|
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,
|
endpoint: process.env.S3_ENDPOINT,
|
||||||
bucket: process.env.S3_BUCKET || 'bigcapital-documents',
|
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');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -12,7 +12,8 @@ export default class SeedAccounts extends TenantSeeder {
|
|||||||
description: this.i18n.__(account.description),
|
description: this.i18n.__(account.description),
|
||||||
currencyCode: this.tenant.metadata.baseCurrency,
|
currencyCode: this.tenant.metadata.baseCurrency,
|
||||||
seededAt: new Date(),
|
seededAt: new Date(),
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return knex('accounts').then(async () => {
|
return knex('accounts').then(async () => {
|
||||||
// Inserts seed entries.
|
// Inserts seed entries.
|
||||||
return knex('accounts').insert(data);
|
return knex('accounts').insert(data);
|
||||||
|
|||||||
@@ -9,28 +9,6 @@ export const TaxPayableAccount = {
|
|||||||
predefined: 1,
|
predefined: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UnearnedRevenueAccount = {
|
|
||||||
name: 'Unearned Revenue',
|
|
||||||
slug: 'unearned-revenue',
|
|
||||||
account_type: 'other-current-liability',
|
|
||||||
parent_account_id: null,
|
|
||||||
code: '50005',
|
|
||||||
active: true,
|
|
||||||
index: 1,
|
|
||||||
predefined: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PrepardExpenses = {
|
|
||||||
name: 'Prepaid Expenses',
|
|
||||||
slug: 'prepaid-expenses',
|
|
||||||
account_type: 'other-current-asset',
|
|
||||||
parent_account_id: null,
|
|
||||||
code: '100010',
|
|
||||||
active: true,
|
|
||||||
index: 1,
|
|
||||||
predefined: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
name: 'Bank Account',
|
name: 'Bank Account',
|
||||||
@@ -345,6 +323,4 @@ export default [
|
|||||||
index: 1,
|
index: 1,
|
||||||
predefined: 0,
|
predefined: 0,
|
||||||
},
|
},
|
||||||
UnearnedRevenueAccount,
|
|
||||||
PrepardExpenses,
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -166,10 +166,3 @@ export interface IBillOpenedPayload {
|
|||||||
oldBill: IBill;
|
oldBill: IBill;
|
||||||
tenantId: number;
|
tenantId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface IBillPrepardExpensesAppliedEventPayload {
|
|
||||||
tenantId: number;
|
|
||||||
billId: number;
|
|
||||||
trx?: Knex.Transaction;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ export interface IBillPayment {
|
|||||||
|
|
||||||
localAmount?: number;
|
localAmount?: number;
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
prepardExpensesAccountId?: number;
|
|
||||||
isPrepardExpense: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBillPaymentEntryDTO {
|
export interface IBillPaymentEntryDTO {
|
||||||
@@ -41,7 +38,6 @@ export interface IBillPaymentEntryDTO {
|
|||||||
|
|
||||||
export interface IBillPaymentDTO {
|
export interface IBillPaymentDTO {
|
||||||
vendorId: number;
|
vendorId: number;
|
||||||
amount: number;
|
|
||||||
paymentAccountId: number;
|
paymentAccountId: number;
|
||||||
paymentNumber?: string;
|
paymentNumber?: string;
|
||||||
paymentDate: Date;
|
paymentDate: Date;
|
||||||
@@ -51,7 +47,6 @@ export interface IBillPaymentDTO {
|
|||||||
entries: IBillPaymentEntryDTO[];
|
entries: IBillPaymentEntryDTO[];
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
attachments?: AttachmentLinkDTO[];
|
attachments?: AttachmentLinkDTO[];
|
||||||
prepardExpensesAccountId?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBillReceivePageEntry {
|
export interface IBillReceivePageEntry {
|
||||||
@@ -124,11 +119,3 @@ export enum IPaymentMadeAction {
|
|||||||
Delete = 'Delete',
|
Delete = 'Delete',
|
||||||
View = 'View',
|
View = 'View',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPaymentPrepardExpensesAppliedEventPayload {
|
|
||||||
tenantId: number;
|
|
||||||
billPaymentId: number;
|
|
||||||
billId: number;
|
|
||||||
appliedAmount: number;
|
|
||||||
trx?: Knex.Transaction;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export interface ILedgerEntry {
|
|||||||
date: Date | string;
|
date: Date | string;
|
||||||
|
|
||||||
transactionType: string;
|
transactionType: string;
|
||||||
transactionSubType?: string;
|
transactionSubType: string;
|
||||||
|
|
||||||
transactionId: number;
|
transactionId: number;
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,8 @@ export interface IPaymentReceive {
|
|||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
localAmount?: number;
|
localAmount?: number;
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
unearnedRevenueAccountId?: number;
|
|
||||||
}
|
}
|
||||||
|
export interface IPaymentReceiveCreateDTO {
|
||||||
interface IPaymentReceivedCommonDTO {
|
|
||||||
unearnedRevenueAccountId?: number;
|
|
||||||
}
|
|
||||||
export interface IPaymentReceiveCreateDTO extends IPaymentReceivedCommonDTO {
|
|
||||||
customerId: number;
|
customerId: number;
|
||||||
paymentDate: Date;
|
paymentDate: Date;
|
||||||
amount: number;
|
amount: number;
|
||||||
@@ -46,7 +41,7 @@ export interface IPaymentReceiveCreateDTO extends IPaymentReceivedCommonDTO {
|
|||||||
attachments?: AttachmentLinkDTO[];
|
attachments?: AttachmentLinkDTO[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPaymentReceiveEditDTO extends IPaymentReceivedCommonDTO {
|
export interface IPaymentReceiveEditDTO {
|
||||||
customerId: number;
|
customerId: number;
|
||||||
paymentDate: Date;
|
paymentDate: Date;
|
||||||
amount: number;
|
amount: number;
|
||||||
@@ -189,11 +184,3 @@ export interface PaymentReceiveMailPresendEvent {
|
|||||||
paymentReceiveId: number;
|
paymentReceiveId: number;
|
||||||
messageOptions: PaymentReceiveMailOptsDTO;
|
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;
|
saleInvoiceId: number;
|
||||||
messageOptions: SendInvoiceMailDTO;
|
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 { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch';
|
||||||
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
|
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
|
||||||
import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize';
|
import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize';
|
||||||
import { AutoApplyUnearnedRevenueOnInvoiceCreated } from '@/services/Sales/PaymentReceives/events/AutoApplyUnearnedRevenueOnInvoiceCreated';
|
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
|
||||||
import { AutoApplyPrepardExpensesOnBillCreated } from '@/services/Purchases/Bills/events/AutoApplyPrepardExpensesOnBillCreated';
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return new EventPublisher();
|
return new EventPublisher();
|
||||||
@@ -276,5 +275,8 @@ export const susbcribers = () => {
|
|||||||
|
|
||||||
// Plaid
|
// Plaid
|
||||||
RecognizeSyncedBankTranasctions,
|
RecognizeSyncedBankTranasctions,
|
||||||
|
|
||||||
|
// Loops
|
||||||
|
LoopsEventsSubscriber
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -525,9 +525,9 @@ export default class Bill extends mixin(TenantModel, [
|
|||||||
return notFoundBillsIds;
|
return notFoundBillsIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
static changePaymentAmount(billId, amount, trx) {
|
static changePaymentAmount(billId, amount) {
|
||||||
const changeMethod = amount > 0 ? 'increment' : 'decrement';
|
const changeMethod = amount > 0 ? 'increment' : 'decrement';
|
||||||
return this.query(trx)
|
return this.query()
|
||||||
.where('id', billId)
|
.where('id', billId)
|
||||||
[changeMethod]('payment_amount', Math.abs(amount));
|
[changeMethod]('payment_amount', Math.abs(amount));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ export default class BillPayment extends mixin(TenantModel, [
|
|||||||
CustomViewBaseModel,
|
CustomViewBaseModel,
|
||||||
ModelSearchable,
|
ModelSearchable,
|
||||||
]) {
|
]) {
|
||||||
prepardExpensesAccountId: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -49,14 +47,6 @@ export default class BillPayment extends mixin(TenantModel, [
|
|||||||
return BillPaymentSettings;
|
return BillPaymentSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Detarmines whether the payment is prepard expense.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get isPrepardExpense() {
|
|
||||||
return !!this.prepardExpensesAccountId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,12 +2,7 @@ import { Account } from 'models';
|
|||||||
import TenantRepository from '@/repositories/TenantRepository';
|
import TenantRepository from '@/repositories/TenantRepository';
|
||||||
import { IAccount } from '@/interfaces';
|
import { IAccount } from '@/interfaces';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import {
|
import { TaxPayableAccount } from '@/database/seeds/data/accounts';
|
||||||
PrepardExpenses,
|
|
||||||
TaxPayableAccount,
|
|
||||||
UnearnedRevenueAccount,
|
|
||||||
} from '@/database/seeds/data/accounts';
|
|
||||||
import { TenantMetadata } from '@/system/models';
|
|
||||||
|
|
||||||
export default class AccountRepository extends TenantRepository {
|
export default class AccountRepository extends TenantRepository {
|
||||||
/**
|
/**
|
||||||
@@ -184,67 +179,4 @@ export default class AccountRepository extends TenantRepository {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds or creates the unearned revenue.
|
|
||||||
* @param {Record<string, string>} extraAttrs
|
|
||||||
* @param {Knex.Transaction} trx
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public async findOrCreateUnearnedRevenue(
|
|
||||||
extraAttrs: Record<string, string> = {},
|
|
||||||
trx?: Knex.Transaction
|
|
||||||
) {
|
|
||||||
// Retrieves the given tenant metadata.
|
|
||||||
const tenantMeta = await TenantMetadata.query().findOne({
|
|
||||||
tenantId: this.tenantId,
|
|
||||||
});
|
|
||||||
const _extraAttrs = {
|
|
||||||
currencyCode: tenantMeta.baseCurrency,
|
|
||||||
...extraAttrs,
|
|
||||||
};
|
|
||||||
let result = await this.model
|
|
||||||
.query(trx)
|
|
||||||
.findOne({ slug: UnearnedRevenueAccount.slug, ..._extraAttrs });
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
result = await this.model.query(trx).insertAndFetch({
|
|
||||||
...UnearnedRevenueAccount,
|
|
||||||
..._extraAttrs,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds or creates the prepard expenses account.
|
|
||||||
* @param {Record<string, string>} extraAttrs
|
|
||||||
* @param {Knex.Transaction} trx
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public async findOrCreatePrepardExpenses(
|
|
||||||
extraAttrs: Record<string, string> = {},
|
|
||||||
trx?: Knex.Transaction
|
|
||||||
) {
|
|
||||||
// Retrieves the given tenant metadata.
|
|
||||||
const tenantMeta = await TenantMetadata.query().findOne({
|
|
||||||
tenantId: this.tenantId,
|
|
||||||
});
|
|
||||||
const _extraAttrs = {
|
|
||||||
currencyCode: tenantMeta.baseCurrency,
|
|
||||||
...extraAttrs,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = await this.model
|
|
||||||
.query(trx)
|
|
||||||
.findOne({ slug: PrepardExpenses.slug, ..._extraAttrs });
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
result = await this.model.query(trx).insertAndFetch({
|
|
||||||
...PrepardExpenses,
|
|
||||||
..._extraAttrs,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,12 @@ import CachableRepository from './CachableRepository';
|
|||||||
|
|
||||||
export default class TenantRepository extends CachableRepository {
|
export default class TenantRepository extends CachableRepository {
|
||||||
repositoryName: string;
|
repositoryName: string;
|
||||||
tenantId: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor method.
|
* Constructor method.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
*/
|
*/
|
||||||
constructor(knex, cache, i18n) {
|
constructor(knex, cache, i18n) {
|
||||||
super(knex, cache, i18n);
|
super(knex, cache, i18n);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setTenantId(tenantId: number) {
|
|
||||||
this.tenantId = tenantId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,24 +38,4 @@ export default class ContactTransfromer extends Transformer {
|
|||||||
? this.formatDate(contact.openingBalanceAt)
|
? 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',
|
'formattedOpeningBalanceAt',
|
||||||
'customerType',
|
'customerType',
|
||||||
'formattedCustomerType',
|
'formattedCustomerType',
|
||||||
'unusedCredit',
|
|
||||||
'formattedUnusedCredit',
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ export class CustomersApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new customer.
|
* Creates a new customer.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {ICustomerNewDTO} customerDTO
|
* @param {ICustomerNewDTO} customerDTO
|
||||||
* @param {ISystemUser} authorizedUser
|
* @param {ISystemUser} authorizedUser
|
||||||
* @returns {Promise<ICustomer>}
|
* @returns {Promise<ICustomer>}
|
||||||
*/
|
*/
|
||||||
public createCustomer = (tenantId: number, customerDTO: ICustomerNewDTO) => {
|
public createCustomer = (tenantId: number, customerDTO: ICustomerNewDTO) => {
|
||||||
@@ -56,9 +56,9 @@ export class CustomersApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits details of the given customer.
|
* Edits details of the given customer.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} customerId
|
* @param {number} customerId
|
||||||
* @param {ICustomerEditDTO} customerDTO
|
* @param {ICustomerEditDTO} customerDTO
|
||||||
* @return {Promise<ICustomer>}
|
* @return {Promise<ICustomer>}
|
||||||
*/
|
*/
|
||||||
public editCustomer = (
|
public editCustomer = (
|
||||||
@@ -75,9 +75,9 @@ export class CustomersApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given customer and associated transactions.
|
* Deletes the given customer and associated transactions.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} customerId
|
* @param {number} customerId
|
||||||
* @param {ISystemUser} authorizedUser
|
* @param {ISystemUser} authorizedUser
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public deleteCustomer = (
|
public deleteCustomer = (
|
||||||
@@ -94,9 +94,9 @@ export class CustomersApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the opening balance of the given customer.
|
* Changes the opening balance of the given customer.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} customerId
|
* @param {number} customerId
|
||||||
* @param {Date|string} openingBalanceEditDTO
|
* @param {Date|string} openingBalanceEditDTO
|
||||||
* @returns {Promise<ICustomer>}
|
* @returns {Promise<ICustomer>}
|
||||||
*/
|
*/
|
||||||
public editOpeningBalance = (
|
public editOpeningBalance = (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
import ContactTransfromer from '../ContactTransformer';
|
import ContactTransfromer from '../ContactTransformer';
|
||||||
|
|
||||||
export default class VendorTransfromer extends ContactTransfromer {
|
export default class VendorTransfromer extends ContactTransfromer {
|
||||||
@@ -9,9 +10,7 @@ export default class VendorTransfromer extends ContactTransfromer {
|
|||||||
return [
|
return [
|
||||||
'formattedBalance',
|
'formattedBalance',
|
||||||
'formattedOpeningBalance',
|
'formattedOpeningBalance',
|
||||||
'formattedOpeningBalanceAt',
|
'formattedOpeningBalanceAt'
|
||||||
'unusedCredit',
|
|
||||||
'formattedUnusedCredit',
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const DEFAULT_VIEWS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
OPENING_BALANCE_DATE_REQUIRED: 'OPENING_BALANCE_DATE_REQUIRED',
|
OPENING_BALANCE_DATE_REQUIRED: 'OPENING_BALANCE_DATE_REQUIRED',
|
||||||
CONTACT_ALREADY_INACTIVE: 'CONTACT_ALREADY_INACTIVE',
|
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 moment from 'moment';
|
||||||
import { sumBy, chain } from 'lodash';
|
import { sumBy } from 'lodash';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import {
|
import { AccountNormal, IBillPayment, ILedgerEntry } from '@/interfaces';
|
||||||
AccountNormal,
|
|
||||||
IBillPayment,
|
|
||||||
IBillPaymentEntry,
|
|
||||||
ILedger,
|
|
||||||
ILedgerEntry,
|
|
||||||
} from '@/interfaces';
|
|
||||||
import Ledger from '@/services/Accounting/Ledger';
|
import Ledger from '@/services/Accounting/Ledger';
|
||||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
@@ -27,7 +21,6 @@ export class BillPaymentGLEntries {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} billPaymentId
|
* @param {number} billPaymentId
|
||||||
* @param {Knex.Transaction} trx
|
* @param {Knex.Transaction} trx
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
*/
|
||||||
public writePaymentGLEntries = async (
|
public writePaymentGLEntries = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
@@ -72,7 +65,6 @@ export class BillPaymentGLEntries {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} billPaymentId
|
* @param {number} billPaymentId
|
||||||
* @param {Knex.Transaction} trx
|
* @param {Knex.Transaction} trx
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
*/
|
||||||
public rewritePaymentGLEntries = async (
|
public rewritePaymentGLEntries = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
@@ -110,7 +102,7 @@ export class BillPaymentGLEntries {
|
|||||||
* @param {IBillPayment} billPayment
|
* @param {IBillPayment} billPayment
|
||||||
* @returns {}
|
* @returns {}
|
||||||
*/
|
*/
|
||||||
private getPaymentCommonEntry = (billPayment: IBillPayment): ILedgerEntry => {
|
private getPaymentCommonEntry = (billPayment: IBillPayment) => {
|
||||||
const formattedDate = moment(billPayment.paymentDate).format('YYYY-MM-DD');
|
const formattedDate = moment(billPayment.paymentDate).format('YYYY-MM-DD');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -135,7 +127,7 @@ export class BillPaymentGLEntries {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the payment total exchange gain/loss.
|
* Calculates the payment total exchange gain/loss.
|
||||||
* @param {IBillPayment} paymentReceive - Payment receive with entries.
|
* @param {IBillPayment} paymentReceive - Payment receive with entries.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
private getPaymentExGainOrLoss = (billPayment: IBillPayment): number => {
|
private getPaymentExGainOrLoss = (billPayment: IBillPayment): number => {
|
||||||
@@ -149,10 +141,10 @@ export class BillPaymentGLEntries {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment exchange gain/loss entries.
|
* Retrieves the payment exchange gain/loss entries.
|
||||||
* @param {IBillPayment} billPayment -
|
* @param {IBillPayment} billPayment -
|
||||||
* @param {number} APAccountId -
|
* @param {number} APAccountId -
|
||||||
* @param {number} gainLossAccountId -
|
* @param {number} gainLossAccountId -
|
||||||
* @param {string} baseCurrency -
|
* @param {string} baseCurrency -
|
||||||
* @returns {ILedgerEntry[]}
|
* @returns {ILedgerEntry[]}
|
||||||
*/
|
*/
|
||||||
private getPaymentExGainOrLossEntries = (
|
private getPaymentExGainOrLossEntries = (
|
||||||
@@ -194,7 +186,7 @@ export class BillPaymentGLEntries {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment deposit GL entry.
|
* Retrieves the payment deposit GL entry.
|
||||||
* @param {IBillPayment} billPayment
|
* @param {IBillPayment} billPayment
|
||||||
* @returns {ILedgerEntry}
|
* @returns {ILedgerEntry}
|
||||||
*/
|
*/
|
||||||
private getPaymentGLEntry = (billPayment: IBillPayment): ILedgerEntry => {
|
private getPaymentGLEntry = (billPayment: IBillPayment): ILedgerEntry => {
|
||||||
@@ -206,7 +198,6 @@ export class BillPaymentGLEntries {
|
|||||||
accountId: billPayment.paymentAccountId,
|
accountId: billPayment.paymentAccountId,
|
||||||
accountNormal: AccountNormal.DEBIT,
|
accountNormal: AccountNormal.DEBIT,
|
||||||
index: 2,
|
index: 2,
|
||||||
indexGroup: 10,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -235,8 +226,8 @@ export class BillPaymentGLEntries {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment GL entries.
|
* Retrieves the payment GL entries.
|
||||||
* @param {IBillPayment} billPayment
|
* @param {IBillPayment} billPayment
|
||||||
* @param {number} APAccountId
|
* @param {number} APAccountId
|
||||||
* @returns {ILedgerEntry[]}
|
* @returns {ILedgerEntry[]}
|
||||||
*/
|
*/
|
||||||
private getPaymentGLEntries = (
|
private getPaymentGLEntries = (
|
||||||
@@ -263,53 +254,10 @@ export class BillPaymentGLEntries {
|
|||||||
return [paymentEntry, payableEntry, ...exGainLossEntries];
|
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.
|
* Retrieves the bill payment ledger.
|
||||||
* @param {IBillPayment} billPayment
|
* @param {IBillPayment} billPayment
|
||||||
* @param {number} APAccountId
|
* @param {number} APAccountId
|
||||||
* @returns {Ledger}
|
* @returns {Ledger}
|
||||||
*/
|
*/
|
||||||
private getBillPaymentLedger = (
|
private getBillPaymentLedger = (
|
||||||
@@ -318,79 +266,12 @@ export class BillPaymentGLEntries {
|
|||||||
gainLossAccountId: number,
|
gainLossAccountId: number,
|
||||||
baseCurrency: string
|
baseCurrency: string
|
||||||
): Ledger => {
|
): Ledger => {
|
||||||
const entries = billPayment.isPrepardExpense
|
const entries = this.getPaymentGLEntries(
|
||||||
? this.getPrepardExpenseGLEntries(APAccountId, billPayment)
|
billPayment,
|
||||||
: this.getPaymentGLEntries(
|
APAccountId,
|
||||||
billPayment,
|
gainLossAccountId,
|
||||||
APAccountId,
|
baseCurrency
|
||||||
gainLossAccountId,
|
);
|
||||||
baseCurrency
|
|
||||||
);
|
|
||||||
return new Ledger(entries);
|
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) {
|
public attach(bus) {
|
||||||
bus.subscribe(events.billPayment.onCreated, this.handleWriteJournalEntries);
|
bus.subscribe(events.billPayment.onCreated, this.handleWriteJournalEntries);
|
||||||
bus.subscribe(
|
|
||||||
events.billPayment.onPrepardExpensesApplied,
|
|
||||||
this.handleWritePrepardExpenseGLEntries
|
|
||||||
);
|
|
||||||
bus.subscribe(
|
bus.subscribe(
|
||||||
events.billPayment.onEdited,
|
events.billPayment.onEdited,
|
||||||
this.handleRewriteJournalEntriesOncePaymentEdited
|
this.handleRewriteJournalEntriesOncePaymentEdited
|
||||||
@@ -32,8 +28,7 @@ export class PaymentWriteGLEntriesSubscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles bill payment writing journal entries once created.
|
* Handle bill payment writing journal entries once created.
|
||||||
* @param {IBillPaymentEventCreatedPayload} payload -
|
|
||||||
*/
|
*/
|
||||||
private handleWriteJournalEntries = async ({
|
private handleWriteJournalEntries = async ({
|
||||||
tenantId,
|
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.
|
* Handle bill payment re-writing journal entries once the payment transaction be edited.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ import { omit, sumBy } from 'lodash';
|
|||||||
import { IBillPayment, IBillPaymentDTO, IVendor } from '@/interfaces';
|
import { IBillPayment, IBillPaymentDTO, IVendor } from '@/interfaces';
|
||||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||||
import { formatDateFields } from '@/utils';
|
import { formatDateFields } from '@/utils';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class CommandBillPaymentDTOTransformer {
|
export class CommandBillPaymentDTOTransformer {
|
||||||
@Inject()
|
@Inject()
|
||||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms create/edit DTO to model.
|
* Transforms create/edit DTO to model.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -27,27 +23,14 @@ export class CommandBillPaymentDTOTransformer {
|
|||||||
vendor: IVendor,
|
vendor: IVendor,
|
||||||
oldBillPayment?: IBillPayment
|
oldBillPayment?: IBillPayment
|
||||||
): Promise<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 initialDTO = {
|
const initialDTO = {
|
||||||
...formatDateFields(omit(billPaymentDTO, ['attachments']), [
|
...formatDateFields(omit(billPaymentDTO, ['attachments']), [
|
||||||
'paymentDate',
|
'paymentDate',
|
||||||
]),
|
]),
|
||||||
appliedAmount,
|
amount: sumBy(billPaymentDTO.entries, 'paymentAmount'),
|
||||||
currencyCode: vendor.currencyCode,
|
currencyCode: vendor.currencyCode,
|
||||||
exchangeRate: billPaymentDTO.exchangeRate || 1,
|
exchangeRate: billPaymentDTO.exchangeRate || 1,
|
||||||
entries: billPaymentDTO.entries,
|
entries: billPaymentDTO.entries,
|
||||||
prepardExpensesAccountId,
|
|
||||||
};
|
};
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<IBillPayment>(tenantId)
|
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 { PaymentReceiveIncrement } from './PaymentReceiveIncrement';
|
||||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||||
import { formatDateFields } from '@/utils';
|
import { formatDateFields } from '@/utils';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PaymentReceiveDTOTransformer {
|
export class PaymentReceiveDTOTransformer {
|
||||||
@@ -24,9 +23,6 @@ export class PaymentReceiveDTOTransformer {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the create payment receive DTO to model object.
|
* Transformes the create payment receive DTO to model object.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -40,8 +36,7 @@ export class PaymentReceiveDTOTransformer {
|
|||||||
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
|
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
|
||||||
oldPaymentReceive?: IPaymentReceive
|
oldPaymentReceive?: IPaymentReceive
|
||||||
): Promise<IPaymentReceive> {
|
): Promise<IPaymentReceive> {
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||||
const appliedAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
|
||||||
|
|
||||||
// Retreive the next invoice number.
|
// Retreive the next invoice number.
|
||||||
const autoNextNumber =
|
const autoNextNumber =
|
||||||
@@ -55,29 +50,17 @@ export class PaymentReceiveDTOTransformer {
|
|||||||
|
|
||||||
this.validators.validatePaymentNoRequire(paymentReceiveNo);
|
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 = {
|
const initialDTO = {
|
||||||
...formatDateFields(omit(paymentReceiveDTO, ['entries', 'attachments']), [
|
...formatDateFields(omit(paymentReceiveDTO, ['entries', 'attachments']), [
|
||||||
'paymentDate',
|
'paymentDate',
|
||||||
]),
|
]),
|
||||||
appliedAmount,
|
amount: paymentAmount,
|
||||||
currencyCode: customer.currencyCode,
|
currencyCode: customer.currencyCode,
|
||||||
...(paymentReceiveNo ? { paymentReceiveNo } : {}),
|
...(paymentReceiveNo ? { paymentReceiveNo } : {}),
|
||||||
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
|
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
|
||||||
entries: paymentReceiveDTO.entries.map((entry) => ({
|
entries: paymentReceiveDTO.entries.map((entry) => ({
|
||||||
...entry,
|
...entry,
|
||||||
})),
|
})),
|
||||||
unearnedRevenueAccountId,
|
|
||||||
};
|
};
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.branchDTOTransform.transformDTO<IPaymentReceive>(tenantId)
|
this.branchDTOTransform.transformDTO<IPaymentReceive>(tenantId)
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import Ledger from '@/services/Accounting/Ledger';
|
import Ledger from '@/services/Accounting/Ledger';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
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 LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
import { TenantMetadata } from '@/system/models';
|
import { TenantMetadata } from '@/system/models';
|
||||||
import { PaymentReceivedGLCommon } from './PaymentReceivedGLCommon';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
export class PaymentReceiveGLEntries {
|
||||||
@Inject()
|
@Inject()
|
||||||
private tenancy: TenancyService;
|
private tenancy: TenancyService;
|
||||||
|
|
||||||
@@ -17,9 +22,9 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes payment GL entries to the storage.
|
* Writes payment GL entries to the storage.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId
|
||||||
* @param {number} paymentReceiveId - Payment received id.
|
* @param {number} paymentReceiveId
|
||||||
* @param {Knex.Transaction} trx - Knex transaction.
|
* @param {Knex.Transaction} trx
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public writePaymentGLEntries = async (
|
public writePaymentGLEntries = async (
|
||||||
@@ -29,19 +34,14 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
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.
|
// Retrieves the payment receive with associated entries.
|
||||||
const paymentReceive = await PaymentReceive.query(trx)
|
const paymentReceive = await PaymentReceive.query(trx)
|
||||||
.findById(paymentReceiveId)
|
.findById(paymentReceiveId)
|
||||||
.withGraphFetched('entries.invoice');
|
.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.
|
// Retrives the payment receive ledger.
|
||||||
const ledger = await this.getPaymentReceiveGLedger(
|
const ledger = await this.getPaymentReceiveGLedger(
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -53,6 +53,25 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
await this.ledgerStorage.commit(tenantId, ledger, trx);
|
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.
|
* Rewrites the given payment receive GL entries.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -73,10 +92,10 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment receive general ledger.
|
* Retrieves the payment receive general ledger.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {IPaymentReceive} paymentReceive -
|
* @param {IPaymentReceive} paymentReceive -
|
||||||
* @param {string} baseCurrencyCode -
|
* @param {string} baseCurrencyCode -
|
||||||
* @param {Knex.Transaction} trx -
|
* @param {Knex.Transaction} trx -
|
||||||
* @returns {Ledger}
|
* @returns {Ledger}
|
||||||
*/
|
*/
|
||||||
public getPaymentReceiveGLedger = async (
|
public getPaymentReceiveGLedger = async (
|
||||||
@@ -107,9 +126,100 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
return new Ledger(ledgerEntries);
|
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.
|
* Retrieves the payment deposit GL entry.
|
||||||
* @param {IPaymentReceive} paymentReceive
|
* @param {IPaymentReceive} paymentReceive
|
||||||
* @returns {ILedgerEntry}
|
* @returns {ILedgerEntry}
|
||||||
*/
|
*/
|
||||||
private getPaymentDepositGLEntry = (
|
private getPaymentDepositGLEntry = (
|
||||||
@@ -128,8 +238,8 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment receivable entry.
|
* Retrieves the payment receivable entry.
|
||||||
* @param {IPaymentReceive} paymentReceive
|
* @param {IPaymentReceive} paymentReceive
|
||||||
* @param {number} ARAccountId
|
* @param {number} ARAccountId
|
||||||
* @returns {ILedgerEntry}
|
* @returns {ILedgerEntry}
|
||||||
*/
|
*/
|
||||||
private getPaymentReceivableEntry = (
|
private getPaymentReceivableEntry = (
|
||||||
@@ -152,15 +262,15 @@ export class PaymentReceiveGLEntries extends PaymentReceivedGLCommon {
|
|||||||
* Records payment receive journal transactions.
|
* Records payment receive journal transactions.
|
||||||
*
|
*
|
||||||
* Invoice payment journals.
|
* Invoice payment journals.
|
||||||
* ------------
|
* --------
|
||||||
* - Account Receivable -> Debit
|
* - Account receivable -> Debit
|
||||||
* - Payment Account [current asset] -> Credit
|
* - Payment account [current asset] -> Credit
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IPaymentReceive} paymentRecieve - Payment receive model.
|
* @param {IPaymentReceive} paymentRecieve - Payment receive model.
|
||||||
* @param {number} ARAccountId - A/R account id.
|
* @param {number} ARAccountId - A/R account id.
|
||||||
* @param {number} exGainOrLossAccountId - Exchange gain/loss account id.
|
* @param {number} exGainOrLossAccountId - Exchange gain/loss account id.
|
||||||
* @param {string} baseCurrency - Base currency code.
|
* @param {string} baseCurrency - Base currency code.
|
||||||
* @returns {Promise<ILedgerEntry>}
|
* @returns {Promise<ILedgerEntry>}
|
||||||
*/
|
*/
|
||||||
public getPaymentReceiveGLEntries = (
|
public getPaymentReceiveGLEntries = (
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export class PaymentReceiveValidators {
|
|||||||
const invoicesIds = paymentReceiveEntries.map(
|
const invoicesIds = paymentReceiveEntries.map(
|
||||||
(e: IPaymentReceiveEntryDTO) => e.invoiceId
|
(e: IPaymentReceiveEntryDTO) => e.invoiceId
|
||||||
);
|
);
|
||||||
|
|
||||||
const storedInvoices = await SaleInvoice.query().whereIn('id', invoicesIds);
|
const storedInvoices = await SaleInvoice.query().whereIn('id', invoicesIds);
|
||||||
|
|
||||||
const storedInvoicesMap = new Map(
|
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -77,12 +77,7 @@ export default class HasTenancyService {
|
|||||||
const knex = this.knex(tenantId);
|
const knex = this.knex(tenantId);
|
||||||
const i18n = this.i18n(tenantId);
|
const i18n = this.i18n(tenantId);
|
||||||
|
|
||||||
const repositories = tenantRepositoriesLoader(knex, cache, i18n);
|
return tenantRepositoriesLoader(knex, cache, i18n);
|
||||||
|
|
||||||
Object.values(repositories).forEach((repository) => {
|
|
||||||
repository.setTenantId(tenantId);
|
|
||||||
});
|
|
||||||
return repositories;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,20 +3,15 @@ import {
|
|||||||
IPaymentReceiveCreatedPayload,
|
IPaymentReceiveCreatedPayload,
|
||||||
IPaymentReceiveDeletedPayload,
|
IPaymentReceiveDeletedPayload,
|
||||||
IPaymentReceiveEditedPayload,
|
IPaymentReceiveEditedPayload,
|
||||||
PaymentReceiveUnearnedRevenueAppliedEventPayload,
|
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import { PaymentReceiveGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceiveGLEntries';
|
import { PaymentReceiveGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceiveGLEntries';
|
||||||
import { PaymentReceivedUnearnedGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceivedUnearnedGLEntries';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class PaymentReceivesWriteGLEntriesSubscriber {
|
export default class PaymentReceivesWriteGLEntriesSubscriber {
|
||||||
@Inject()
|
@Inject()
|
||||||
private paymentReceiveGLEntries: PaymentReceiveGLEntries;
|
private paymentReceiveGLEntries: PaymentReceiveGLEntries;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private paymentReceivedUnearnedGLEntries: PaymentReceivedUnearnedGLEntries;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches events with handlers.
|
* Attaches events with handlers.
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +32,6 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle journal entries writing once the payment receive created.
|
* Handle journal entries writing once the payment receive created.
|
||||||
* @param {IPaymentReceiveCreatedPayload} payload -
|
|
||||||
*/
|
*/
|
||||||
private handleWriteJournalEntriesOnceCreated = async ({
|
private handleWriteJournalEntriesOnceCreated = async ({
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -49,21 +43,14 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
|||||||
paymentReceiveId,
|
paymentReceiveId,
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
await this.paymentReceivedUnearnedGLEntries.writePaymentGLEntries(
|
|
||||||
tenantId,
|
|
||||||
paymentReceiveId,
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle journal entries writing once the payment receive edited.
|
* Handle journal entries writing once the payment receive edited.
|
||||||
* @param {IPaymentReceiveEditedPayload} payload -
|
|
||||||
*/
|
*/
|
||||||
private handleOverwriteJournalEntriesOnceEdited = async ({
|
private handleOverwriteJournalEntriesOnceEdited = async ({
|
||||||
tenantId,
|
tenantId,
|
||||||
paymentReceive,
|
paymentReceive,
|
||||||
paymentReceiveId,
|
|
||||||
trx,
|
trx,
|
||||||
}: IPaymentReceiveEditedPayload) => {
|
}: IPaymentReceiveEditedPayload) => {
|
||||||
await this.paymentReceiveGLEntries.rewritePaymentGLEntries(
|
await this.paymentReceiveGLEntries.rewritePaymentGLEntries(
|
||||||
@@ -71,16 +58,10 @@ export default class PaymentReceivesWriteGLEntriesSubscriber {
|
|||||||
paymentReceive.id,
|
paymentReceive.id,
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
await this.paymentReceivedUnearnedGLEntries.rewritePaymentGLEntries(
|
|
||||||
tenantId,
|
|
||||||
paymentReceiveId,
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles revert journal entries once deleted.
|
* Handles revert journal entries once deleted.
|
||||||
* @param {IPaymentReceiveDeletedPayload} payload -
|
|
||||||
*/
|
*/
|
||||||
private handleRevertJournalEntriesOnceDeleted = async ({
|
private handleRevertJournalEntriesOnceDeleted = async ({
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ export default {
|
|||||||
baseCurrencyUpdated: 'onOrganizationBaseCurrencyUpdated',
|
baseCurrencyUpdated: 'onOrganizationBaseCurrencyUpdated',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User subscription events.
|
||||||
|
*/
|
||||||
|
subscription: {
|
||||||
|
onSubscribed: 'onOrganizationSubscribed',
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tenants managment service.
|
* Tenants managment service.
|
||||||
*/
|
*/
|
||||||
@@ -142,8 +149,6 @@ export default {
|
|||||||
|
|
||||||
onMailReminderSend: 'onSaleInvoiceMailReminderSend',
|
onMailReminderSend: 'onSaleInvoiceMailReminderSend',
|
||||||
onMailReminderSent: 'onSaleInvoiceMailReminderSent',
|
onMailReminderSent: 'onSaleInvoiceMailReminderSent',
|
||||||
|
|
||||||
onUnearnedRevenueApplied: 'onSaleInvoiceUnearnedRevenue',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,8 +237,6 @@ export default {
|
|||||||
onPreMailSend: 'onPaymentReceivePreMailSend',
|
onPreMailSend: 'onPaymentReceivePreMailSend',
|
||||||
onMailSend: 'onPaymentReceiveMailSend',
|
onMailSend: 'onPaymentReceiveMailSend',
|
||||||
onMailSent: 'onPaymentReceiveMailSent',
|
onMailSent: 'onPaymentReceiveMailSent',
|
||||||
|
|
||||||
onUnearnedRevenueApplied: 'onPaymentReceivedUnearnedRevenue',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,8 +257,6 @@ export default {
|
|||||||
|
|
||||||
onOpening: 'onBillOpening',
|
onOpening: 'onBillOpening',
|
||||||
onOpened: 'onBillOpened',
|
onOpened: 'onBillOpened',
|
||||||
|
|
||||||
onPrepardExpensesApplied: 'onBillPrepardExpensesApplied'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,8 +274,6 @@ export default {
|
|||||||
|
|
||||||
onPublishing: 'onBillPaymentPublishing',
|
onPublishing: 'onBillPaymentPublishing',
|
||||||
onPublished: 'onBillPaymentPublished',
|
onPublished: 'onBillPaymentPublished',
|
||||||
|
|
||||||
onPrepardExpensesApplied: 'onBillPaymentPrepardExpensesApplied'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ export const ACCOUNT_TYPE = {
|
|||||||
BANK: 'bank',
|
BANK: 'bank',
|
||||||
ACCOUNTS_RECEIVABLE: 'accounts-receivable',
|
ACCOUNTS_RECEIVABLE: 'accounts-receivable',
|
||||||
INVENTORY: 'inventory',
|
INVENTORY: 'inventory',
|
||||||
OTHER_CURRENT_ASSET: 'other-current-asset',
|
OTHER_CURRENT_ASSET: 'other-ACCOUNT_PARENT_TYPE.CURRENT_ASSET',
|
||||||
FIXED_ASSET: 'fixed-asset',
|
FIXED_ASSET: 'fixed-asset',
|
||||||
NON_CURRENT_ASSET: 'non-current-asset',
|
NON_CURRENT_ASSET: 'non-ACCOUNT_PARENT_TYPE.CURRENT_ASSET',
|
||||||
|
|
||||||
ACCOUNTS_PAYABLE: 'accounts-payable',
|
ACCOUNTS_PAYABLE: 'accounts-payable',
|
||||||
CREDIT_CARD: 'credit-card',
|
CREDIT_CARD: 'credit-card',
|
||||||
|
|||||||
@@ -160,13 +160,6 @@ export function useCustomersTableColumns() {
|
|||||||
width: 85,
|
width: 85,
|
||||||
clickable: true,
|
clickable: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'credit_balance',
|
|
||||||
Header: 'Credit Balance',
|
|
||||||
accessor: 'formatted_unused_credit',
|
|
||||||
width: 100,
|
|
||||||
align: 'right',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'balance',
|
id: 'balance',
|
||||||
Header: intl.get('receivable_balance'),
|
Header: intl.get('receivable_balance'),
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ExcessPaymentDialog } from './dialogs/PaymentMadeExcessDialog';
|
|
||||||
|
|
||||||
export function PaymentMadeDialogs() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ExcessPaymentDialog dialogName={'payment-made-excessed-payment'} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Formik, Form, FormikHelpers } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { sumBy, defaultTo } from 'lodash';
|
import { sumBy, defaultTo } from 'lodash';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
@@ -14,7 +14,6 @@ import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
|
|||||||
import PaymentMadeFooter from './PaymentMadeFooter';
|
import PaymentMadeFooter from './PaymentMadeFooter';
|
||||||
import PaymentMadeFormBody from './PaymentMadeFormBody';
|
import PaymentMadeFormBody from './PaymentMadeFormBody';
|
||||||
import PaymentMadeFormTopBar from './PaymentMadeFormTopBar';
|
import PaymentMadeFormTopBar from './PaymentMadeFormTopBar';
|
||||||
import { PaymentMadeDialogs } from './PaymentMadeDialogs';
|
|
||||||
|
|
||||||
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
|
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
|
||||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||||
@@ -22,7 +21,6 @@ import { compose, orderingLinesIndexes } from '@/utils';
|
|||||||
|
|
||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EditPaymentMadeFormSchema,
|
EditPaymentMadeFormSchema,
|
||||||
@@ -33,7 +31,6 @@ import {
|
|||||||
transformToEditForm,
|
transformToEditForm,
|
||||||
transformErrors,
|
transformErrors,
|
||||||
transformFormToRequest,
|
transformFormToRequest,
|
||||||
getPaymentExcessAmountFromValues,
|
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,9 +42,6 @@ function PaymentMadeForm({
|
|||||||
|
|
||||||
// #withCurrentOrganization
|
// #withCurrentOrganization
|
||||||
organization: { base_currency },
|
organization: { base_currency },
|
||||||
|
|
||||||
// #withDialogActions
|
|
||||||
openDialog,
|
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -60,7 +54,6 @@ function PaymentMadeForm({
|
|||||||
submitPayload,
|
submitPayload,
|
||||||
createPaymentMadeMutate,
|
createPaymentMadeMutate,
|
||||||
editPaymentMadeMutate,
|
editPaymentMadeMutate,
|
||||||
isExcessConfirmed,
|
|
||||||
} = usePaymentMadeFormContext();
|
} = usePaymentMadeFormContext();
|
||||||
|
|
||||||
// Form initial values.
|
// Form initial values.
|
||||||
@@ -83,11 +76,13 @@ function PaymentMadeForm({
|
|||||||
// Handle the form submit.
|
// Handle the form submit.
|
||||||
const handleSubmitForm = (
|
const handleSubmitForm = (
|
||||||
values,
|
values,
|
||||||
{ setSubmitting, resetForm, setFieldError }: FormikHelpers<any>,
|
{ setSubmitting, resetForm, setFieldError },
|
||||||
) => {
|
) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
// Total payment amount of entries.
|
||||||
|
const totalPaymentAmount = sumBy(values.entries, 'payment_amount');
|
||||||
|
|
||||||
if (values.amount <= 0) {
|
if (totalPaymentAmount <= 0) {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
@@ -95,16 +90,6 @@ function PaymentMadeForm({
|
|||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const excessAmount = getPaymentExcessAmountFromValues(values);
|
|
||||||
|
|
||||||
// Show the confirmation popup if the excess amount bigger than zero and
|
|
||||||
// has not been confirmed yet.
|
|
||||||
if (excessAmount > 0 && !isExcessConfirmed) {
|
|
||||||
openDialog('payment-made-excessed-payment');
|
|
||||||
setSubmitting(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Transformes the form values to request body.
|
// Transformes the form values to request body.
|
||||||
const form = transformFormToRequest(values);
|
const form = transformFormToRequest(values);
|
||||||
|
|
||||||
@@ -134,12 +119,11 @@ function PaymentMadeForm({
|
|||||||
}
|
}
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isNewMode) {
|
if (!isNewMode) {
|
||||||
return editPaymentMadeMutate([paymentMadeId, form])
|
editPaymentMadeMutate([paymentMadeId, form]).then(onSaved).catch(onError);
|
||||||
.then(onSaved)
|
|
||||||
.catch(onError);
|
|
||||||
} else {
|
} else {
|
||||||
return createPaymentMadeMutate(form).then(onSaved).catch(onError);
|
createPaymentMadeMutate(form).then(onSaved).catch(onError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -165,7 +149,6 @@ function PaymentMadeForm({
|
|||||||
<PaymentMadeFormBody />
|
<PaymentMadeFormBody />
|
||||||
<PaymentMadeFooter />
|
<PaymentMadeFooter />
|
||||||
<PaymentMadeFloatingActions />
|
<PaymentMadeFloatingActions />
|
||||||
<PaymentMadeDialogs />
|
|
||||||
</PaymentMadeInnerProvider>
|
</PaymentMadeInnerProvider>
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
@@ -180,5 +163,4 @@ export default compose(
|
|||||||
preferredPaymentAccount: parseInt(billPaymentSettings?.withdrawalAccount),
|
preferredPaymentAccount: parseInt(billPaymentSettings?.withdrawalAccount),
|
||||||
})),
|
})),
|
||||||
withCurrentOrganization(),
|
withCurrentOrganization(),
|
||||||
withDialogActions,
|
|
||||||
)(PaymentMadeForm);
|
)(PaymentMadeForm);
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useFormikContext } from 'formik';
|
|
||||||
import {
|
import {
|
||||||
T,
|
T,
|
||||||
TotalLines,
|
TotalLines,
|
||||||
TotalLine,
|
TotalLine,
|
||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
FormatNumber,
|
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { usePaymentMadeExcessAmount, usePaymentMadeTotals } from './utils';
|
import { usePaymentMadeTotals } from './utils';
|
||||||
|
|
||||||
export function PaymentMadeFormFooterRight() {
|
export function PaymentMadeFormFooterRight() {
|
||||||
const { formattedSubtotal, formattedTotal } = usePaymentMadeTotals();
|
const { formattedSubtotal, formattedTotal } = usePaymentMadeTotals();
|
||||||
const excessAmount = usePaymentMadeExcessAmount();
|
|
||||||
const {
|
|
||||||
values: { currency_code: currencyCode },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentMadeTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<PaymentMadeTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
@@ -31,11 +25,6 @@ export function PaymentMadeFormFooterRight() {
|
|||||||
value={formattedTotal}
|
value={formattedTotal}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
|
||||||
title={'Excess Amount'}
|
|
||||||
value={<FormatNumber value={excessAmount} currency={currencyCode} />}
|
|
||||||
textStyle={TotalLineTextStyle.Regular}
|
|
||||||
/>
|
|
||||||
</PaymentMadeTotalLines>
|
</PaymentMadeTotalLines>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { Money, FormattedMessage as T } from '@/components';
|
import { Money, FormattedMessage as T } from '@/components';
|
||||||
|
|
||||||
import PaymentMadeFormHeaderFields from './PaymentMadeFormHeaderFields';
|
import PaymentMadeFormHeaderFields from './PaymentMadeFormHeaderFields';
|
||||||
import { usePaymentmadeTotalAmount } from './utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment made header form.
|
* Payment made header form.
|
||||||
@@ -14,10 +14,11 @@ import { usePaymentmadeTotalAmount } from './utils';
|
|||||||
function PaymentMadeFormHeader() {
|
function PaymentMadeFormHeader() {
|
||||||
// Formik form context.
|
// Formik form context.
|
||||||
const {
|
const {
|
||||||
values: { currency_code },
|
values: { entries, currency_code },
|
||||||
} = useFormikContext();
|
} = useFormikContext();
|
||||||
|
|
||||||
const totalAmount = usePaymentmadeTotalAmount();
|
// Calculate the payment amount of the entries.
|
||||||
|
const amountPaid = useMemo(() => sumBy(entries, 'payment_amount'), [entries]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
@@ -29,9 +30,8 @@ function PaymentMadeFormHeader() {
|
|||||||
<span class="big-amount__label">
|
<span class="big-amount__label">
|
||||||
<T id={'amount_received'} />
|
<T id={'amount_received'} />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h1 class="big-amount__number">
|
<h1 class="big-amount__number">
|
||||||
<Money amount={totalAmount} currency={currency_code} />
|
<Money amount={amountPaid} currency={currency_code} />
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { isEmpty, toSafeInteger } from 'lodash';
|
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
||||||
import { FormattedMessage as T, VendorsSelect } from '@/components';
|
import { FormattedMessage as T, VendorsSelect } from '@/components';
|
||||||
|
import { toSafeInteger } from 'lodash';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -68,7 +68,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
|||||||
const fullAmount = safeSumBy(newEntries, 'payment_amount');
|
const fullAmount = safeSumBy(newEntries, 'payment_amount');
|
||||||
|
|
||||||
setFieldValue('entries', newEntries);
|
setFieldValue('entries', newEntries);
|
||||||
setFieldValue('amount', fullAmount);
|
setFieldValue('full_amount', fullAmount);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the full-amount field blur.
|
// Handles the full-amount field blur.
|
||||||
@@ -115,10 +115,10 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ------------ Full amount ------------ */}
|
{/* ------------ Full amount ------------ */}
|
||||||
<Field name={'amount'}>
|
<Field name={'full_amount'}>
|
||||||
{({
|
{({
|
||||||
form: {
|
form: {
|
||||||
values: { currency_code, entries },
|
values: { currency_code },
|
||||||
},
|
},
|
||||||
field: { value },
|
field: { value },
|
||||||
meta: { error, touched },
|
meta: { error, touched },
|
||||||
@@ -129,30 +129,28 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
|||||||
className={('form-group--full-amount', Classes.FILL)}
|
className={('form-group--full-amount', Classes.FILL)}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
labelInfo={<Hint />}
|
labelInfo={<Hint />}
|
||||||
helperText={<ErrorMessage name="amount" />}
|
helperText={<ErrorMessage name="full_amount" />}
|
||||||
>
|
>
|
||||||
<ControlGroup>
|
<ControlGroup>
|
||||||
<InputPrependText text={currency_code} />
|
<InputPrependText text={currency_code} />
|
||||||
<MoneyInputGroup
|
<MoneyInputGroup
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setFieldValue('amount', value);
|
setFieldValue('full_amount', value);
|
||||||
}}
|
}}
|
||||||
onBlurValue={onFullAmountBlur}
|
onBlurValue={onFullAmountBlur}
|
||||||
/>
|
/>
|
||||||
</ControlGroup>
|
</ControlGroup>
|
||||||
|
|
||||||
{!isEmpty(entries) && (
|
<Button
|
||||||
<Button
|
onClick={handleReceiveFullAmountClick}
|
||||||
onClick={handleReceiveFullAmountClick}
|
className={'receive-full-amount'}
|
||||||
className={'receive-full-amount'}
|
small={true}
|
||||||
small={true}
|
minimal={true}
|
||||||
minimal={true}
|
>
|
||||||
>
|
<T id={'receive_full_amount'} /> (
|
||||||
<T id={'receive_full_amount'} /> (
|
<Money amount={payableFullAmount} currency={currency_code} />)
|
||||||
<Money amount={payableFullAmount} currency={currency_code} />)
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
@@ -71,8 +71,6 @@ function PaymentMadeFormProvider({ query, paymentMadeId, ...props }) {
|
|||||||
|
|
||||||
const isFeatureLoading = isBranchesLoading;
|
const isFeatureLoading = isBranchesLoading;
|
||||||
|
|
||||||
const [isExcessConfirmed, setIsExcessConfirmed] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
paymentMadeId,
|
paymentMadeId,
|
||||||
@@ -100,9 +98,6 @@ function PaymentMadeFormProvider({ query, paymentMadeId, ...props }) {
|
|||||||
|
|
||||||
setSubmitPayload,
|
setSubmitPayload,
|
||||||
setPaymentVendorId,
|
setPaymentVendorId,
|
||||||
|
|
||||||
isExcessConfirmed,
|
|
||||||
setIsExcessConfirmed,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Dialog, DialogSuspense } from '@/components';
|
|
||||||
import withDialogRedux from '@/components/DialogReduxConnect';
|
|
||||||
import { compose } from '@/utils';
|
|
||||||
|
|
||||||
const ExcessPaymentDialogContent = React.lazy(() =>
|
|
||||||
import('./PaymentMadeExcessDialogContent').then((module) => ({
|
|
||||||
default: module.ExcessPaymentDialogContent,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exess payment dialog of the payment made form.
|
|
||||||
*/
|
|
||||||
function ExcessPaymentDialogRoot({ dialogName, isOpen }) {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
name={dialogName}
|
|
||||||
title={'Excess Payment'}
|
|
||||||
isOpen={isOpen}
|
|
||||||
canEscapeJeyClose={true}
|
|
||||||
autoFocus={true}
|
|
||||||
style={{ width: 500 }}
|
|
||||||
>
|
|
||||||
<DialogSuspense>
|
|
||||||
<ExcessPaymentDialogContent dialogName={dialogName} />
|
|
||||||
</DialogSuspense>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExcessPaymentDialog = compose(withDialogRedux())(
|
|
||||||
ExcessPaymentDialogRoot,
|
|
||||||
);
|
|
||||||
|
|
||||||
ExcessPaymentDialog.displayName = 'ExcessPaymentDialog';
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import * as R from 'ramda';
|
|
||||||
import * as Yup from 'yup';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { Button, Classes, Intent } from '@blueprintjs/core';
|
|
||||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
|
||||||
import { AccountsSelect, FFormGroup, 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';
|
|
||||||
|
|
||||||
function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
|
||||||
const {
|
|
||||||
setFieldValue,
|
|
||||||
submitForm,
|
|
||||||
values: { currency_code: currencyCode },
|
|
||||||
} = useFormikContext();
|
|
||||||
const { setIsExcessConfirmed } = usePaymentMadeFormContext();
|
|
||||||
|
|
||||||
// Handles the form submitting.
|
|
||||||
const handleSubmit = (
|
|
||||||
values: ExcessPaymentValues,
|
|
||||||
{ setSubmitting }: FormikHelpers<ExcessPaymentValues>,
|
|
||||||
) => {
|
|
||||||
setFieldValue(values.accountId);
|
|
||||||
setSubmitting(true);
|
|
||||||
setIsExcessConfirmed(true);
|
|
||||||
|
|
||||||
return submitForm().then(() => {
|
|
||||||
setSubmitting(false);
|
|
||||||
closeDialog(dialogName);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// Handle close button click.
|
|
||||||
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}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<ExcessPaymentDialogContentForm
|
|
||||||
excessAmount={
|
|
||||||
<FormatNumber value={excessAmount} currency={currencyCode} />
|
|
||||||
}
|
|
||||||
onClose={handleCloseBtn}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExcessPaymentDialogContent = R.compose(withDialogActions)(
|
|
||||||
ExcessPaymentDialogContentRoot,
|
|
||||||
);
|
|
||||||
|
|
||||||
interface ExcessPaymentDialogContentFormProps {
|
|
||||||
excessAmount: string | number | React.ReactNode;
|
|
||||||
onClose?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExcessPaymentDialogContentForm({
|
|
||||||
excessAmount,
|
|
||||||
onClose,
|
|
||||||
}: ExcessPaymentDialogContentFormProps) {
|
|
||||||
const { submitForm, isSubmitting } = useFormikContext();
|
|
||||||
const { accounts } = usePaymentMadeFormContext();
|
|
||||||
|
|
||||||
const handleCloseBtn = () => {
|
|
||||||
onClose && onClose();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<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.
|
|
||||||
</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}>
|
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
|
||||||
<Button
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
loading={isSubmitting}
|
|
||||||
onClick={() => submitForm()}
|
|
||||||
>
|
|
||||||
Save Payment
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleCloseBtn}>Cancel</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 +0,0 @@
|
|||||||
export * from './PaymentMadeExcessDialog';
|
|
||||||
@@ -37,7 +37,7 @@ export const defaultPaymentMadeEntry = {
|
|||||||
|
|
||||||
// Default initial values of payment made.
|
// Default initial values of payment made.
|
||||||
export const defaultPaymentMade = {
|
export const defaultPaymentMade = {
|
||||||
amount: '',
|
full_amount: '',
|
||||||
vendor_id: '',
|
vendor_id: '',
|
||||||
payment_account_id: '',
|
payment_account_id: '',
|
||||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
@@ -53,10 +53,10 @@ export const defaultPaymentMade = {
|
|||||||
|
|
||||||
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
||||||
const attachments = transformAttachmentsToForm(paymentMade);
|
const attachments = transformAttachmentsToForm(paymentMade);
|
||||||
const appliedAmount = safeSumBy(paymentMadeEntries, 'payment_amount');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...transformToForm(paymentMade, defaultPaymentMade),
|
...transformToForm(paymentMade, defaultPaymentMade),
|
||||||
|
full_amount: safeSumBy(paymentMadeEntries, 'payment_amount'),
|
||||||
entries: [
|
entries: [
|
||||||
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
||||||
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
|
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
|
||||||
@@ -177,30 +177,6 @@ export const usePaymentMadeTotals = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePaymentmadeTotalAmount = () => {
|
|
||||||
const {
|
|
||||||
values: { amount },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentMadeAppliedAmount = () => {
|
|
||||||
const {
|
|
||||||
values: { entries },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
// Retrieves the invoice entries total.
|
|
||||||
return React.useMemo(() => sumBy(entries, 'payment_amount'), [entries]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentMadeExcessAmount = () => {
|
|
||||||
const appliedAmount = usePaymentMadeAppliedAmount();
|
|
||||||
const totalAmount = usePaymentmadeTotalAmount();
|
|
||||||
|
|
||||||
return Math.abs(totalAmount - appliedAmount);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the bill has foreign customer.
|
* Detarmines whether the bill has foreign customer.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@@ -215,10 +191,3 @@ export const usePaymentMadeIsForeignCustomer = () => {
|
|||||||
);
|
);
|
||||||
return isForeignCustomer;
|
return isForeignCustomer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPaymentExcessAmountFromValues = (values) => {
|
|
||||||
const appliedAmount = sumBy(values.entries, 'payment_amount');
|
|
||||||
const totalAmount = values.amount;
|
|
||||||
|
|
||||||
return Math.abs(totalAmount - appliedAmount);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo, useRef } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@@ -21,7 +21,6 @@ import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider';
|
|||||||
|
|
||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EditPaymentReceiveFormSchema,
|
EditPaymentReceiveFormSchema,
|
||||||
@@ -37,7 +36,6 @@ import {
|
|||||||
transformFormToRequest,
|
transformFormToRequest,
|
||||||
transformErrors,
|
transformErrors,
|
||||||
resetFormState,
|
resetFormState,
|
||||||
getExceededAmountFromValues,
|
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
|
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
|
||||||
|
|
||||||
@@ -53,9 +51,6 @@ function PaymentReceiveForm({
|
|||||||
|
|
||||||
// #withCurrentOrganization
|
// #withCurrentOrganization
|
||||||
organization: { base_currency },
|
organization: { base_currency },
|
||||||
|
|
||||||
// #withDialogActions
|
|
||||||
openDialog,
|
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -68,7 +63,6 @@ function PaymentReceiveForm({
|
|||||||
submitPayload,
|
submitPayload,
|
||||||
editPaymentReceiveMutate,
|
editPaymentReceiveMutate,
|
||||||
createPaymentReceiveMutate,
|
createPaymentReceiveMutate,
|
||||||
isExcessConfirmed,
|
|
||||||
} = usePaymentReceiveFormContext();
|
} = usePaymentReceiveFormContext();
|
||||||
|
|
||||||
// Payment receive number.
|
// Payment receive number.
|
||||||
@@ -100,16 +94,18 @@ function PaymentReceiveForm({
|
|||||||
preferredDepositAccount,
|
preferredDepositAccount,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle form submit.
|
// Handle form submit.
|
||||||
const handleSubmitForm = (
|
const handleSubmitForm = (
|
||||||
values,
|
values,
|
||||||
{ setSubmitting, resetForm, setFieldError },
|
{ setSubmitting, resetForm, setFieldError },
|
||||||
) => {
|
) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const exceededAmount = getExceededAmountFromValues(values);
|
|
||||||
|
|
||||||
// Validates the amount should be bigger than zero.
|
// Calculates the total payment amount of entries.
|
||||||
if (values.amount <= 0) {
|
const totalPaymentAmount = sumBy(values.entries, 'payment_amount');
|
||||||
|
|
||||||
|
if (totalPaymentAmount <= 0) {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
@@ -117,13 +113,6 @@ function PaymentReceiveForm({
|
|||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Show the confirm popup if the excessed amount bigger than zero and
|
|
||||||
// excess confirmation has not been confirmed yet.
|
|
||||||
if (exceededAmount > 0 && !isExcessConfirmed) {
|
|
||||||
setSubmitting(false);
|
|
||||||
openDialog('payment-received-excessed-payment');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Transformes the form values to request body.
|
// Transformes the form values to request body.
|
||||||
const form = transformFormToRequest(values);
|
const form = transformFormToRequest(values);
|
||||||
|
|
||||||
@@ -159,11 +148,11 @@ function PaymentReceiveForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (paymentReceiveId) {
|
if (paymentReceiveId) {
|
||||||
return editPaymentReceiveMutate([paymentReceiveId, form])
|
editPaymentReceiveMutate([paymentReceiveId, form])
|
||||||
.then(onSaved)
|
.then(onSaved)
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
} else {
|
} else {
|
||||||
return createPaymentReceiveMutate(form).then(onSaved).catch(onError);
|
createPaymentReceiveMutate(form).then(onSaved).catch(onError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -213,5 +202,4 @@ export default compose(
|
|||||||
preferredDepositAccount: paymentReceiveSettings?.preferredDepositAccount,
|
preferredDepositAccount: paymentReceiveSettings?.preferredDepositAccount,
|
||||||
})),
|
})),
|
||||||
withCurrentOrganization(),
|
withCurrentOrganization(),
|
||||||
withDialogActions,
|
|
||||||
)(PaymentReceiveForm);
|
)(PaymentReceiveForm);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import PaymentReceiveNumberDialog from '@/containers/Dialogs/PaymentReceiveNumberDialog';
|
import PaymentReceiveNumberDialog from '@/containers/Dialogs/PaymentReceiveNumberDialog';
|
||||||
import { ExcessPaymentDialog } from './dialogs/ExcessPaymentDialog';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive form dialogs.
|
* Payment receive form dialogs.
|
||||||
@@ -22,12 +21,9 @@ export default function PaymentReceiveFormDialogs() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<PaymentReceiveNumberDialog
|
||||||
<PaymentReceiveNumberDialog
|
dialogName={'payment-receive-number-form'}
|
||||||
dialogName={'payment-receive-number-form'}
|
onConfirm={handleUpdatePaymentNumber}
|
||||||
onConfirm={handleUpdatePaymentNumber}
|
/>
|
||||||
/>
|
|
||||||
<ExcessPaymentDialog dialogName={'payment-received-excessed-payment'} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,11 @@ import {
|
|||||||
TotalLine,
|
TotalLine,
|
||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
FormatNumber,
|
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import {
|
import { usePaymentReceiveTotals } from './utils';
|
||||||
usePaymentReceiveTotals,
|
|
||||||
usePaymentReceivedTotalExceededAmount,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
export function PaymentReceiveFormFootetRight() {
|
export function PaymentReceiveFormFootetRight() {
|
||||||
const { formattedSubtotal, formattedTotal } = usePaymentReceiveTotals();
|
const { formattedSubtotal, formattedTotal } = usePaymentReceiveTotals();
|
||||||
const exceededAmount = usePaymentReceivedTotalExceededAmount();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentReceiveTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<PaymentReceiveTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
@@ -30,11 +25,6 @@ export function PaymentReceiveFormFootetRight() {
|
|||||||
value={formattedTotal}
|
value={formattedTotal}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
|
||||||
title={'Exceeded Amount'}
|
|
||||||
value={<FormatNumber value={exceededAmount} />}
|
|
||||||
textStyle={TotalLineTextStyle.Regular}
|
|
||||||
/>
|
|
||||||
</PaymentReceiveTotalLines>
|
</PaymentReceiveTotalLines>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,15 @@ function PaymentReceiveFormHeader() {
|
|||||||
function PaymentReceiveFormBigTotal() {
|
function PaymentReceiveFormBigTotal() {
|
||||||
// Formik form context.
|
// Formik form context.
|
||||||
const {
|
const {
|
||||||
values: { currency_code, amount },
|
values: { currency_code, entries },
|
||||||
} = useFormikContext();
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Calculates the total payment amount from due amount.
|
||||||
|
const paymentFullAmount = useMemo(
|
||||||
|
() => sumBy(entries, 'payment_amount'),
|
||||||
|
[entries],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
|
||||||
<div class="big-amount">
|
<div class="big-amount">
|
||||||
@@ -40,7 +46,7 @@ function PaymentReceiveFormBigTotal() {
|
|||||||
<T id={'amount_received'} />
|
<T id={'amount_received'} />
|
||||||
</span>
|
</span>
|
||||||
<h1 class="big-amount__number">
|
<h1 class="big-amount__number">
|
||||||
<Money amount={amount} currency={currency_code} />
|
<Money amount={paymentFullAmount} currency={currency_code} />
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import { DashboardInsider } from '@/components';
|
import { DashboardInsider } from '@/components';
|
||||||
@@ -74,8 +74,6 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
const { mutateAsync: editPaymentReceiveMutate } = useEditPaymentReceive();
|
const { mutateAsync: editPaymentReceiveMutate } = useEditPaymentReceive();
|
||||||
const { mutateAsync: createPaymentReceiveMutate } = useCreatePaymentReceive();
|
const { mutateAsync: createPaymentReceiveMutate } = useCreatePaymentReceive();
|
||||||
|
|
||||||
const [isExcessConfirmed, setIsExcessConfirmed] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
paymentReceiveId,
|
paymentReceiveId,
|
||||||
@@ -99,9 +97,6 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
|
|
||||||
editPaymentReceiveMutate,
|
editPaymentReceiveMutate,
|
||||||
createPaymentReceiveMutate,
|
createPaymentReceiveMutate,
|
||||||
|
|
||||||
isExcessConfirmed,
|
|
||||||
setIsExcessConfirmed,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { isEmpty, toSafeInteger } from 'lodash';
|
import { toSafeInteger } from 'lodash';
|
||||||
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -124,11 +124,11 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ------------ Full amount ------------ */}
|
{/* ------------ Full amount ------------ */}
|
||||||
<Field name={'amount'}>
|
<Field name={'full_amount'}>
|
||||||
{({
|
{({
|
||||||
form: {
|
form: {
|
||||||
setFieldValue,
|
setFieldValue,
|
||||||
values: { currency_code, entries },
|
values: { currency_code },
|
||||||
},
|
},
|
||||||
field: { value, onChange },
|
field: { value, onChange },
|
||||||
meta: { error, touched },
|
meta: { error, touched },
|
||||||
@@ -146,23 +146,21 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
<MoneyInputGroup
|
<MoneyInputGroup
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setFieldValue('amount', value);
|
setFieldValue('full_amount', value);
|
||||||
}}
|
}}
|
||||||
onBlurValue={onFullAmountBlur}
|
onBlurValue={onFullAmountBlur}
|
||||||
/>
|
/>
|
||||||
</ControlGroup>
|
</ControlGroup>
|
||||||
|
|
||||||
{!isEmpty(entries) && (
|
<Button
|
||||||
<Button
|
onClick={handleReceiveFullAmountClick}
|
||||||
onClick={handleReceiveFullAmountClick}
|
className={'receive-full-amount'}
|
||||||
className={'receive-full-amount'}
|
small={true}
|
||||||
small={true}
|
minimal={true}
|
||||||
minimal={true}
|
>
|
||||||
>
|
<T id={'receive_full_amount'} /> (
|
||||||
<T id={'receive_full_amount'} /> (
|
<Money amount={totalDueAmount} currency={currency_code} />)
|
||||||
<Money amount={totalDueAmount} currency={currency_code} />)
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Dialog, DialogSuspense } from '@/components';
|
|
||||||
import withDialogRedux from '@/components/DialogReduxConnect';
|
|
||||||
import { compose } from '@/utils';
|
|
||||||
|
|
||||||
const ExcessPaymentDialogContent = React.lazy(() =>
|
|
||||||
import('./ExcessPaymentDialogContent').then((module) => ({
|
|
||||||
default: module.ExcessPaymentDialogContent,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Excess payment dialog of the payment received form.
|
|
||||||
*/
|
|
||||||
function ExcessPaymentDialogRoot({ dialogName, isOpen }) {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
name={dialogName}
|
|
||||||
title={'Excess Payment'}
|
|
||||||
isOpen={isOpen}
|
|
||||||
canEscapeJeyClose={true}
|
|
||||||
autoFocus={true}
|
|
||||||
style={{ width: 500 }}
|
|
||||||
>
|
|
||||||
<DialogSuspense>
|
|
||||||
<ExcessPaymentDialogContent dialogName={dialogName} />
|
|
||||||
</DialogSuspense>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExcessPaymentDialog = compose(withDialogRedux())(
|
|
||||||
ExcessPaymentDialogRoot,
|
|
||||||
);
|
|
||||||
|
|
||||||
ExcessPaymentDialog.displayName = 'ExcessPaymentDialog';
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
// @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 { 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';
|
|
||||||
|
|
||||||
export function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
|
|
||||||
const {
|
|
||||||
setFieldValue,
|
|
||||||
submitForm,
|
|
||||||
values: { currency_code: currencyCode },
|
|
||||||
} = useFormikContext();
|
|
||||||
const { setIsExcessConfirmed } = usePaymentReceiveFormContext();
|
|
||||||
const initialAccountId = useDefaultExcessPaymentDeposit();
|
|
||||||
const exceededAmount = usePaymentReceivedTotalExceededAmount();
|
|
||||||
|
|
||||||
const handleSubmit = (
|
|
||||||
values: ExcessPaymentValues,
|
|
||||||
{ setSubmitting }: FormikHelpers<ExcessPaymentValues>,
|
|
||||||
) => {
|
|
||||||
setSubmitting(true);
|
|
||||||
setIsExcessConfirmed(true);
|
|
||||||
setFieldValue('unearned_revenue_account_id', values.accountId);
|
|
||||||
|
|
||||||
submitForm().then(() => {
|
|
||||||
closeDialog(dialogName);
|
|
||||||
setSubmitting(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleClose = () => {
|
|
||||||
closeDialog(dialogName);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
...initialValues,
|
|
||||||
accountId: initialAccountId,
|
|
||||||
}}
|
|
||||||
validationSchema={Schema}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<ExcessPaymentDialogContentForm
|
|
||||||
exceededAmount={
|
|
||||||
<FormatNumber value={exceededAmount} currency={currencyCode} />
|
|
||||||
}
|
|
||||||
onClose={handleClose}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExcessPaymentDialogContent = R.compose(withDialogActions)(
|
|
||||||
ExcessPaymentDialogContentRoot,
|
|
||||||
);
|
|
||||||
|
|
||||||
function ExcessPaymentDialogContentForm({ onClose, exceededAmount }) {
|
|
||||||
const { accounts } = usePaymentReceiveFormContext();
|
|
||||||
const { submitForm, isSubmitting } = useFormikContext();
|
|
||||||
|
|
||||||
const handleCloseBtn = () => {
|
|
||||||
onClose && onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<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.
|
|
||||||
</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}>
|
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
|
||||||
<Button
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
loading={isSubmitting}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
onClick={() => submitForm()}
|
|
||||||
>
|
|
||||||
Save Payment
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleCloseBtn}>Cancel</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}, []);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './ExcessPaymentDialog';
|
|
||||||
@@ -42,13 +42,12 @@ export const defaultPaymentReceive = {
|
|||||||
// Holds the payment number that entered manually only.
|
// Holds the payment number that entered manually only.
|
||||||
payment_receive_no_manually: '',
|
payment_receive_no_manually: '',
|
||||||
statement: '',
|
statement: '',
|
||||||
amount: '',
|
full_amount: '',
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
branch_id: '',
|
branch_id: '',
|
||||||
exchange_rate: 1,
|
exchange_rate: 1,
|
||||||
entries: [],
|
entries: [],
|
||||||
attachments: [],
|
attachments: []
|
||||||
unearned_revenue_account_id: '',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultRequestPaymentEntry = {
|
export const defaultRequestPaymentEntry = {
|
||||||
@@ -250,30 +249,6 @@ export const usePaymentReceiveTotals = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePaymentReceivedTotalAppliedAmount = () => {
|
|
||||||
const {
|
|
||||||
values: { entries },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
// Retrieves the invoice entries total.
|
|
||||||
return React.useMemo(() => sumBy(entries, 'payment_amount'), [entries]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentReceivedTotalAmount = () => {
|
|
||||||
const {
|
|
||||||
values: { amount },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentReceivedTotalExceededAmount = () => {
|
|
||||||
const totalAmount = usePaymentReceivedTotalAmount();
|
|
||||||
const totalApplied = usePaymentReceivedTotalAppliedAmount();
|
|
||||||
|
|
||||||
return Math.abs(totalAmount - totalApplied);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the payment has foreign customer.
|
* Detarmines whether the payment has foreign customer.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@@ -298,11 +273,3 @@ 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,
|
width: 85,
|
||||||
clickable: true,
|
clickable: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'credit_balance',
|
|
||||||
Header: 'Credit Balance',
|
|
||||||
accessor: 'formatted_unused_credit',
|
|
||||||
width: 100,
|
|
||||||
align: 'right',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'balance',
|
id: 'balance',
|
||||||
Header: intl.get('receivable_balance'),
|
Header: intl.get('receivable_balance'),
|
||||||
|
|||||||
Reference in New Issue
Block a user