mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67c63a4fa | ||
|
|
0025dcf8d4 | ||
|
|
81995dc94f | ||
|
|
3fcc70c1d8 | ||
|
|
a986c7a250 | ||
|
|
37e25a8061 | ||
|
|
cfba628465 | ||
|
|
3d200f4d7d | ||
|
|
8cab012324 |
@@ -141,6 +141,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"bug"
|
"bug"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "mittalsam98",
|
||||||
|
"name": "Sachin Mittal",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/42431274?v=4",
|
||||||
|
"profile": "https://myself.vercel.app/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ export class BankingRulesController extends BaseController {
|
|||||||
body('assign_account_id').isInt({ min: 0 }),
|
body('assign_account_id').isInt({ min: 0 }),
|
||||||
body('assign_payee').isString().optional({ nullable: true }),
|
body('assign_payee').isString().optional({ nullable: true }),
|
||||||
body('assign_memo').isString().optional({ nullable: true }),
|
body('assign_memo').isString().optional({ nullable: true }),
|
||||||
|
|
||||||
body('recognition').isBoolean().toBoolean().optional({ nullable: true }),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/excluded',
|
'/excluded',
|
||||||
[],
|
[
|
||||||
|
query('account_id').optional().isNumeric().toInt(),
|
||||||
|
query('page').optional().isNumeric().toInt(),
|
||||||
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.getExcludedBankTransactions.bind(this)
|
this.getExcludedBankTransactions.bind(this)
|
||||||
);
|
);
|
||||||
@@ -177,7 +181,7 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<Response | void> {
|
): Promise<Response | void> {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter = this.matchedBodyData(req);
|
const filter = this.matchedQueryData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data =
|
const data =
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { NextFunction, Request, Response, Router } from 'express';
|
import { NextFunction, Request, Response, Router } from 'express';
|
||||||
|
import { query } from 'express-validator';
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
import BaseController from '@/api/controllers/BaseController';
|
||||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||||
|
|
||||||
@@ -14,7 +15,16 @@ export class RecognizedTransactionsController extends BaseController {
|
|||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get('/', this.getRecognizedTransactions.bind(this));
|
router.get(
|
||||||
|
'/',
|
||||||
|
[
|
||||||
|
query('page').optional().isNumeric().toInt(),
|
||||||
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
query('account_id').optional().isNumeric().toInt(),
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
this.getRecognizedTransactions.bind(this)
|
||||||
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/transactions/:uncategorizedTransactionId',
|
'/transactions/:uncategorizedTransactionId',
|
||||||
this.getRecognizedTransaction.bind(this)
|
this.getRecognizedTransaction.bind(this)
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentRecei
|
|||||||
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
||||||
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
||||||
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
|
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
|
||||||
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/RecognizeTransactionsJob';
|
import { ReregonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RerecognizeTransactionsJob';
|
||||||
|
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RecognizeTransactionsJob';
|
||||||
|
import { RevertRegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RevertRecognizedTransactionsJob';
|
||||||
|
|
||||||
export default ({ agenda }: { agenda: Agenda }) => {
|
export default ({ agenda }: { agenda: Agenda }) => {
|
||||||
new ResetPasswordMailJob(agenda);
|
new ResetPasswordMailJob(agenda);
|
||||||
@@ -31,6 +33,8 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
|||||||
new ImportDeleteExpiredFilesJobs(agenda);
|
new ImportDeleteExpiredFilesJobs(agenda);
|
||||||
new SendVerifyMailJob(agenda);
|
new SendVerifyMailJob(agenda);
|
||||||
new RegonizeTransactionsJob(agenda);
|
new RegonizeTransactionsJob(agenda);
|
||||||
|
new ReregonizeTransactionsJob(agenda);
|
||||||
|
new RevertRegonizeTransactionsJob(agenda);
|
||||||
|
|
||||||
agenda.start().then(() => {
|
agenda.start().then(() => {
|
||||||
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ export class RecognizeSyncedBankTranasctions extends EventSubscriber {
|
|||||||
runAfterTransaction(trx, async () => {
|
runAfterTransaction(trx, async () => {
|
||||||
await this.recognizeTranasctionsService.recognizeTransactions(
|
await this.recognizeTranasctionsService.recognizeTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
batch
|
null,
|
||||||
|
{ batch }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { castArray, isEmpty } from 'lodash';
|
||||||
import UncategorizedCashflowTransaction from '@/models/UncategorizedCashflowTransaction';
|
import UncategorizedCashflowTransaction from '@/models/UncategorizedCashflowTransaction';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { transformToMapBy } from '@/utils';
|
import { transformToMapBy } from '@/utils';
|
||||||
import { PromisePool } from '@supercharge/promise-pool';
|
import { PromisePool } from '@supercharge/promise-pool';
|
||||||
import { BankRule } from '@/models/BankRule';
|
import { BankRule } from '@/models/BankRule';
|
||||||
import { bankRulesMatchTransaction } from './_utils';
|
import { bankRulesMatchTransaction } from './_utils';
|
||||||
|
import { RecognizeTransactionsCriteria } from './_types';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class RecognizeTranasctionsService {
|
export class RecognizeTranasctionsService {
|
||||||
@@ -48,24 +50,42 @@ export class RecognizeTranasctionsService {
|
|||||||
/**
|
/**
|
||||||
* Regonized the uncategorized transactions.
|
* Regonized the uncategorized transactions.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
|
* @param {number|Array<number>} ruleId - The target rule id/ids.
|
||||||
|
* @param {RecognizeTransactionsCriteria}
|
||||||
* @param {Knex.Transaction} trx -
|
* @param {Knex.Transaction} trx -
|
||||||
*/
|
*/
|
||||||
public async recognizeTransactions(
|
public async recognizeTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
batch: string = '',
|
ruleId?: number | Array<number>,
|
||||||
|
transactionsCriteria?: RecognizeTransactionsCriteria,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction
|
||||||
) {
|
) {
|
||||||
const { UncategorizedCashflowTransaction, BankRule } =
|
const { UncategorizedCashflowTransaction, BankRule } =
|
||||||
this.tenancy.models(tenantId);
|
this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const uncategorizedTranasctions =
|
const uncategorizedTranasctions =
|
||||||
await UncategorizedCashflowTransaction.query().onBuild((query) => {
|
await UncategorizedCashflowTransaction.query(trx).onBuild((query) => {
|
||||||
query.where('recognized_transaction_id', null);
|
query.modify('notRecognized');
|
||||||
query.where('categorized', false);
|
query.modify('notCategorized');
|
||||||
|
|
||||||
if (batch) query.where('batch', batch);
|
// Filter the transactions based on the given criteria.
|
||||||
|
if (transactionsCriteria?.batch) {
|
||||||
|
query.where('batch', transactionsCriteria.batch);
|
||||||
|
}
|
||||||
|
if (transactionsCriteria?.accountId) {
|
||||||
|
query.where('accountId', transactionsCriteria.accountId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const bankRules = await BankRule.query().withGraphFetched('conditions');
|
|
||||||
|
const bankRules = await BankRule.query(trx).onBuild((q) => {
|
||||||
|
const rulesIds = castArray(ruleId);
|
||||||
|
|
||||||
|
if (!isEmpty(rulesIds)) {
|
||||||
|
q.whereIn('id', rulesIds);
|
||||||
|
}
|
||||||
|
q.withGraphFetched('conditions');
|
||||||
|
});
|
||||||
|
|
||||||
const bankRulesByAccountId = transformToMapBy(
|
const bankRulesByAccountId = transformToMapBy(
|
||||||
bankRules,
|
bankRules,
|
||||||
'applyIfAccountId'
|
'applyIfAccountId'
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { castArray } from 'lodash';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import UnitOfWork from '@/services/UnitOfWork';
|
||||||
|
import { RevertRecognizedTransactionsCriteria } from './_types';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class RevertRecognizedTransactions {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private uow: UnitOfWork;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revert and unlinks the recognized transactions based on the given bank rule
|
||||||
|
* and transactions criteria..
|
||||||
|
* @param {number} tenantId - Tenant id.
|
||||||
|
* @param {number|Array<number>} bankRuleId - Bank rule id.
|
||||||
|
* @param {RevertRecognizedTransactionsCriteria} transactionsCriteria -
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async revertRecognizedTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
ruleId?: number | Array<number>,
|
||||||
|
transactionsCriteria?: RevertRecognizedTransactionsCriteria,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
|
const { UncategorizedCashflowTransaction, RecognizedBankTransaction } =
|
||||||
|
this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const rulesIds = castArray(ruleId);
|
||||||
|
|
||||||
|
return this.uow.withTransaction(
|
||||||
|
tenantId,
|
||||||
|
async (trx: Knex.Transaction) => {
|
||||||
|
// Retrieves all the recognized transactions of the banbk rule.
|
||||||
|
const uncategorizedTransactions =
|
||||||
|
await UncategorizedCashflowTransaction.query(trx).onBuild((q) => {
|
||||||
|
q.withGraphJoined('recognizedTransaction');
|
||||||
|
q.whereNotNull('recognizedTransaction.id');
|
||||||
|
|
||||||
|
if (rulesIds.length > 0) {
|
||||||
|
q.whereIn('recognizedTransaction.bankRuleId', rulesIds);
|
||||||
|
}
|
||||||
|
if (transactionsCriteria?.accountId) {
|
||||||
|
q.where('accountId', transactionsCriteria.accountId);
|
||||||
|
}
|
||||||
|
if (transactionsCriteria?.batch) {
|
||||||
|
q.where('batch', transactionsCriteria.batch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const uncategorizedTransactionIds = uncategorizedTransactions.map(
|
||||||
|
(r) => r.id
|
||||||
|
);
|
||||||
|
// Unlink the recongized transactions out of uncategorized transactions.
|
||||||
|
await UncategorizedCashflowTransaction.query(trx)
|
||||||
|
.whereIn('id', uncategorizedTransactionIds)
|
||||||
|
.patch({
|
||||||
|
recognizedTransactionId: null,
|
||||||
|
});
|
||||||
|
// Delete the recognized bank transactions that assocaited to bank rule.
|
||||||
|
await RecognizedBankTransaction.query(trx)
|
||||||
|
.whereIn('uncategorizedTransactionId', uncategorizedTransactionIds)
|
||||||
|
.delete();
|
||||||
|
},
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export interface RevertRecognizedTransactionsCriteria {
|
||||||
|
batch?: string;
|
||||||
|
accountId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface RecognizeTransactionsCriteria {
|
||||||
|
batch?: string;
|
||||||
|
accountId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -41,14 +41,10 @@ export class TriggerRecognizedTransactions {
|
|||||||
*/
|
*/
|
||||||
private async recognizedTransactionsOnRuleCreated({
|
private async recognizedTransactionsOnRuleCreated({
|
||||||
tenantId,
|
tenantId,
|
||||||
createRuleDTO,
|
bankRule,
|
||||||
}: IBankRuleEventCreatedPayload) {
|
}: IBankRuleEventCreatedPayload) {
|
||||||
const payload = { tenantId };
|
const payload = { tenantId, ruleId: bankRule.id };
|
||||||
|
|
||||||
// Cannot run recognition if the option is not enabled.
|
|
||||||
if (createRuleDTO.recognition) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,14 +55,14 @@ export class TriggerRecognizedTransactions {
|
|||||||
private async recognizedTransactionsOnRuleEdited({
|
private async recognizedTransactionsOnRuleEdited({
|
||||||
tenantId,
|
tenantId,
|
||||||
editRuleDTO,
|
editRuleDTO,
|
||||||
|
ruleId,
|
||||||
}: IBankRuleEventEditedPayload) {
|
}: IBankRuleEventEditedPayload) {
|
||||||
const payload = { tenantId };
|
const payload = { tenantId, ruleId };
|
||||||
|
|
||||||
// Cannot run recognition if the option is not enabled.
|
await this.agenda.now(
|
||||||
if (!editRuleDTO.recognition) {
|
'rerecognize-uncategorized-transactions-job',
|
||||||
return;
|
payload
|
||||||
}
|
);
|
||||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,9 +71,13 @@ export class TriggerRecognizedTransactions {
|
|||||||
*/
|
*/
|
||||||
private async recognizedTransactionsOnRuleDeleted({
|
private async recognizedTransactionsOnRuleDeleted({
|
||||||
tenantId,
|
tenantId,
|
||||||
|
ruleId,
|
||||||
}: IBankRuleEventDeletedPayload) {
|
}: IBankRuleEventDeletedPayload) {
|
||||||
const payload = { tenantId };
|
const payload = { tenantId, ruleId };
|
||||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
await this.agenda.now(
|
||||||
|
'revert-recognized-uncategorized-transactions-job',
|
||||||
|
payload
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,7 +91,7 @@ export class TriggerRecognizedTransactions {
|
|||||||
}: IImportFileCommitedEventPayload) {
|
}: IImportFileCommitedEventPayload) {
|
||||||
const importFile = await Import.query().findOne({ importId });
|
const importFile = await Import.query().findOne({ importId });
|
||||||
const batch = importFile.paramsParsed.batch;
|
const batch = importFile.paramsParsed.batch;
|
||||||
const payload = { tenantId, batch };
|
const payload = { tenantId, transactionsCriteria: { batch } };
|
||||||
|
|
||||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Container, { Service } from 'typedi';
|
import Container, { Service } from 'typedi';
|
||||||
import { RecognizeTranasctionsService } from './RecognizeTranasctionsService';
|
import { RecognizeTranasctionsService } from '../RecognizeTranasctionsService';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class RegonizeTransactionsJob {
|
export class RegonizeTransactionsJob {
|
||||||
@@ -18,11 +18,15 @@ export class RegonizeTransactionsJob {
|
|||||||
* Triggers sending invoice mail.
|
* Triggers sending invoice mail.
|
||||||
*/
|
*/
|
||||||
private handler = async (job, done: Function) => {
|
private handler = async (job, done: Function) => {
|
||||||
const { tenantId, batch } = job.attrs.data;
|
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||||
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await regonizeTransactions.recognizeTransactions(tenantId, batch);
|
await regonizeTransactions.recognizeTransactions(
|
||||||
|
tenantId,
|
||||||
|
ruleId,
|
||||||
|
transactionsCriteria
|
||||||
|
);
|
||||||
done();
|
done();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import Container, { Service } from 'typedi';
|
||||||
|
import { RecognizeTranasctionsService } from '../RecognizeTranasctionsService';
|
||||||
|
import { RevertRecognizedTransactions } from '../RevertRecognizedTransactions';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ReregonizeTransactionsJob {
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
constructor(agenda) {
|
||||||
|
agenda.define(
|
||||||
|
'rerecognize-uncategorized-transactions-job',
|
||||||
|
{ priority: 'high', concurrency: 2 },
|
||||||
|
this.handler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers sending invoice mail.
|
||||||
|
*/
|
||||||
|
private handler = async (job, done: Function) => {
|
||||||
|
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||||
|
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
||||||
|
const revertRegonizedTransactions = Container.get(
|
||||||
|
RevertRecognizedTransactions
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await revertRegonizedTransactions.revertRecognizedTransactions(
|
||||||
|
tenantId,
|
||||||
|
ruleId,
|
||||||
|
transactionsCriteria
|
||||||
|
);
|
||||||
|
await regonizeTransactions.recognizeTransactions(
|
||||||
|
tenantId,
|
||||||
|
ruleId,
|
||||||
|
transactionsCriteria
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import Container, { Service } from 'typedi';
|
||||||
|
import { RevertRecognizedTransactions } from '../RevertRecognizedTransactions';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class RevertRegonizeTransactionsJob {
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
constructor(agenda) {
|
||||||
|
agenda.define(
|
||||||
|
'revert-recognized-uncategorized-transactions-job',
|
||||||
|
{ priority: 'high', concurrency: 2 },
|
||||||
|
this.handler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers sending invoice mail.
|
||||||
|
*/
|
||||||
|
private handler = async (job, done: Function) => {
|
||||||
|
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||||
|
const revertRegonizedTransactions = Container.get(
|
||||||
|
RevertRecognizedTransactions
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await revertRegonizedTransactions.revertRecognizedTransactions(
|
||||||
|
tenantId,
|
||||||
|
ruleId,
|
||||||
|
transactionsCriteria
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@ export class CreateBankRuleService {
|
|||||||
await this.eventPublisher.emitAsync(events.bankRules.onCreated, {
|
await this.eventPublisher.emitAsync(events.bankRules.onCreated, {
|
||||||
tenantId,
|
tenantId,
|
||||||
createRuleDTO,
|
createRuleDTO,
|
||||||
|
bankRule,
|
||||||
trx,
|
trx,
|
||||||
} as IBankRuleEventCreatedPayload);
|
} as IBankRuleEventCreatedPayload);
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Knex } from 'knex';
|
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
import UnitOfWork from '@/services/UnitOfWork';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class UnlinkBankRuleRecognizedTransactions {
|
|
||||||
@Inject()
|
|
||||||
private tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private uow: UnitOfWork;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlinks the given bank rule out of recognized transactions.
|
|
||||||
* @param {number} tenantId - Tenant id.
|
|
||||||
* @param {number} bankRuleId - Bank rule id.
|
|
||||||
* @param {Knex.Transaction} trx - Knex transaction.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async unlinkBankRuleOutRecognizedTransactions(
|
|
||||||
tenantId: number,
|
|
||||||
bankRuleId: number,
|
|
||||||
trx?: Knex.Transaction
|
|
||||||
): Promise<void> {
|
|
||||||
const { UncategorizedCashflowTransaction, RecognizedBankTransaction } =
|
|
||||||
this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
return this.uow.withTransaction(
|
|
||||||
tenantId,
|
|
||||||
async (trx: Knex.Transaction) => {
|
|
||||||
// Retrieves all the recognized transactions of the banbk rule.
|
|
||||||
const recognizedTransactions = await RecognizedBankTransaction.query(
|
|
||||||
trx
|
|
||||||
).where('bankRuleId', bankRuleId);
|
|
||||||
|
|
||||||
const uncategorizedTransactionIds = recognizedTransactions.map(
|
|
||||||
(r) => r.uncategorizedTransactionId
|
|
||||||
);
|
|
||||||
// Unlink the recongized transactions out of uncategorized transactions.
|
|
||||||
await UncategorizedCashflowTransaction.query(trx)
|
|
||||||
.whereIn('id', uncategorizedTransactionIds)
|
|
||||||
.patch({
|
|
||||||
recognizedTransactionId: null,
|
|
||||||
});
|
|
||||||
// Delete the recognized bank transactions that assocaited to bank rule.
|
|
||||||
await RecognizedBankTransaction.query(trx)
|
|
||||||
.where({ bankRuleId })
|
|
||||||
.delete();
|
|
||||||
},
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import { UnlinkBankRuleRecognizedTransactions } from '../UnlinkBankRuleRecognizedTransactions';
|
|
||||||
import { IBankRuleEventDeletingPayload } from '../types';
|
import { IBankRuleEventDeletingPayload } from '../types';
|
||||||
|
import { RevertRecognizedTransactions } from '../../RegonizeTranasctions/RevertRecognizedTransactions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class UnlinkBankRuleOnDeleteBankRule {
|
export class UnlinkBankRuleOnDeleteBankRule {
|
||||||
@Inject()
|
@Inject()
|
||||||
private unlinkBankRule: UnlinkBankRuleRecognizedTransactions;
|
private revertRecognizedTransactionsService: RevertRecognizedTransactions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor method.
|
* Constructor method.
|
||||||
@@ -26,7 +26,7 @@ export class UnlinkBankRuleOnDeleteBankRule {
|
|||||||
tenantId,
|
tenantId,
|
||||||
ruleId,
|
ruleId,
|
||||||
}: IBankRuleEventDeletingPayload) {
|
}: IBankRuleEventDeletingPayload) {
|
||||||
await this.unlinkBankRule.unlinkBankRuleOutRecognizedTransactions(
|
await this.revertRecognizedTransactionsService.revertRecognizedTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
ruleId
|
ruleId
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export enum BankRuleApplyIfTransactionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IBankRule {
|
export interface IBankRule {
|
||||||
|
id?: number;
|
||||||
name: string;
|
name: string;
|
||||||
order?: number;
|
order?: number;
|
||||||
applyIfAccountId: number;
|
applyIfAccountId: number;
|
||||||
@@ -71,8 +72,6 @@ export interface IBankRuleCommonDTO {
|
|||||||
assignAccountId: number;
|
assignAccountId: number;
|
||||||
assignPayee?: string;
|
assignPayee?: string;
|
||||||
assignMemo?: string;
|
assignMemo?: string;
|
||||||
|
|
||||||
recognition?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreateBankRuleDTO extends IBankRuleCommonDTO {}
|
export interface ICreateBankRuleDTO extends IBankRuleCommonDTO {}
|
||||||
@@ -86,6 +85,7 @@ export interface IBankRuleEventCreatingPayload {
|
|||||||
export interface IBankRuleEventCreatedPayload {
|
export interface IBankRuleEventCreatedPayload {
|
||||||
tenantId: number;
|
tenantId: number;
|
||||||
createRuleDTO: ICreateBankRuleDTO;
|
createRuleDTO: ICreateBankRuleDTO;
|
||||||
|
bankRule: IBankRule;
|
||||||
trx?: Knex.Transaction;
|
trx?: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ function RecognizedTransactionsTableBoot({
|
|||||||
hasNextPage: hasUncategorizedTransactionsNextPage,
|
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||||
} = useRecognizedBankTransactionsInfinity({
|
} = useRecognizedBankTransactionsInfinity({
|
||||||
page_size: 50,
|
page_size: 50,
|
||||||
|
account_id: accountId,
|
||||||
});
|
});
|
||||||
// Memorized the cashflow account transactions.
|
// Memorized the cashflow account transactions.
|
||||||
const recognizedTransactions = React.useMemo(
|
const recognizedTransactions = React.useMemo(
|
||||||
|
|||||||
@@ -10,18 +10,16 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useInvoiceAggregatedTaxRates, useInvoiceTotals } from './utils';
|
import { useInvoiceAggregatedTaxRates } from './utils';
|
||||||
import { TaxType } from '@/interfaces/TaxRates';
|
import { TaxType } from '@/interfaces/TaxRates';
|
||||||
|
import {
|
||||||
|
InvoiceDueAmountFormatted,
|
||||||
|
InvoicePaidAmountFormatted,
|
||||||
|
InvoiceSubTotalFormatted,
|
||||||
|
InvoiceTotalFormatted,
|
||||||
|
} from './components';
|
||||||
|
|
||||||
export function InvoiceFormFooterRight() {
|
export function InvoiceFormFooterRight() {
|
||||||
// Calculate the total due amount of invoice entries.
|
|
||||||
const {
|
|
||||||
formattedSubtotal,
|
|
||||||
formattedTotal,
|
|
||||||
formattedDueTotal,
|
|
||||||
formattedPaymentTotal,
|
|
||||||
} = useInvoiceTotals();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
values: { inclusive_exclusive_tax, currency_code },
|
values: { inclusive_exclusive_tax, currency_code },
|
||||||
} = useFormikContext();
|
} = useFormikContext();
|
||||||
@@ -38,7 +36,7 @@ export function InvoiceFormFooterRight() {
|
|||||||
: 'Subtotal'}
|
: 'Subtotal'}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
value={formattedSubtotal}
|
value={<InvoiceSubTotalFormatted />}
|
||||||
/>
|
/>
|
||||||
{taxEntries.map((tax, index) => (
|
{taxEntries.map((tax, index) => (
|
||||||
<TotalLine
|
<TotalLine
|
||||||
@@ -50,18 +48,18 @@ export function InvoiceFormFooterRight() {
|
|||||||
))}
|
))}
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={`Total (${currency_code})`}
|
title={`Total (${currency_code})`}
|
||||||
value={formattedTotal}
|
value={<InvoiceTotalFormatted />}
|
||||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'invoice_form.label.payment_amount'} />}
|
title={<T id={'invoice_form.label.payment_amount'} />}
|
||||||
value={formattedPaymentTotal}
|
value={<InvoicePaidAmountFormatted />}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'invoice_form.label.due_amount'} />}
|
title={<T id={'invoice_form.label.due_amount'} />}
|
||||||
value={formattedDueTotal}
|
value={<InvoiceDueAmountFormatted />}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
</InvoiceTotalLines>
|
</InvoiceTotalLines>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import InvoiceFormHeaderFields from './InvoiceFormHeaderFields';
|
|||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { PageFormBigNumber } from '@/components';
|
import { PageFormBigNumber } from '@/components';
|
||||||
import { useInvoiceSubtotal } from './utils';
|
import { useInvoiceDueAmount } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice form header section.
|
* Invoice form header section.
|
||||||
@@ -32,7 +32,7 @@ function InvoiceFormBigTotal() {
|
|||||||
} = useFormikContext();
|
} = useFormikContext();
|
||||||
|
|
||||||
// Calculate the total due amount of invoice entries.
|
// Calculate the total due amount of invoice entries.
|
||||||
const totalDueAmount = useInvoiceSubtotal();
|
const totalDueAmount = useInvoiceDueAmount();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageFormBigNumber
|
<PageFormBigNumber
|
||||||
|
|||||||
@@ -4,14 +4,21 @@ import intl from 'react-intl-universal';
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Button } from '@blueprintjs/core';
|
import { Button } from '@blueprintjs/core';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { ExchangeRateInputGroup } from '@/components';
|
import { ExchangeRateInputGroup, FormatNumber } from '@/components';
|
||||||
import { useCurrentOrganization } from '@/hooks/state';
|
import { useCurrentOrganization } from '@/hooks/state';
|
||||||
import { useInvoiceIsForeignCustomer, useInvoiceTotal } from './utils';
|
import {
|
||||||
import withSettings from '@/containers/Settings/withSettings';
|
useInvoiceCurrencyCode,
|
||||||
|
useInvoiceDueAmount,
|
||||||
|
useInvoiceIsForeignCustomer,
|
||||||
|
useInvoicePaidAmount,
|
||||||
|
useInvoiceSubtotal,
|
||||||
|
useInvoiceTotal,
|
||||||
|
} from './utils';
|
||||||
import { useUpdateEffect } from '@/hooks';
|
import { useUpdateEffect } from '@/hooks';
|
||||||
import { transactionNumber } from '@/utils';
|
import { transactionNumber } from '@/utils';
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
|
||||||
import { DialogsName } from '@/constants/dialogs';
|
import { DialogsName } from '@/constants/dialogs';
|
||||||
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
import {
|
import {
|
||||||
useSyncExRateToForm,
|
useSyncExRateToForm,
|
||||||
withExchangeRateFetchingLoading,
|
withExchangeRateFetchingLoading,
|
||||||
@@ -109,3 +116,47 @@ export const InvoiceExchangeRateSync = R.compose(withDialogActions)(
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Renders the invoice formatted total.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const InvoiceTotalFormatted = () => {
|
||||||
|
const currencyCode = useInvoiceCurrencyCode();
|
||||||
|
const total = useInvoiceTotal();
|
||||||
|
|
||||||
|
return <FormatNumber value={total} currency={currencyCode} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the invoice formatted subtotal.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const InvoiceSubTotalFormatted = () => {
|
||||||
|
const currencyCode = useInvoiceCurrencyCode();
|
||||||
|
const subTotal = useInvoiceSubtotal();
|
||||||
|
|
||||||
|
return <FormatNumber value={subTotal} currency={currencyCode} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the invoice formatted due amount.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const InvoiceDueAmountFormatted = () => {
|
||||||
|
const currencyCode = useInvoiceCurrencyCode();
|
||||||
|
const dueAmount = useInvoiceDueAmount();
|
||||||
|
|
||||||
|
return <FormatNumber value={dueAmount} currency={currencyCode} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the invoice formatted paid amount.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const InvoicePaidAmountFormatted = () => {
|
||||||
|
const currencyCode = useInvoiceCurrencyCode();
|
||||||
|
const paidAmount = useInvoicePaidAmount();
|
||||||
|
|
||||||
|
return <FormatNumber value={paidAmount} currency={currencyCode} />;
|
||||||
|
};
|
||||||
|
|||||||
@@ -269,59 +269,6 @@ export const useInvoiceSubtotal = () => {
|
|||||||
return React.useMemo(() => getEntriesTotal(entries), [entries]);
|
return React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Retreives the invoice totals.
|
|
||||||
*/
|
|
||||||
export const useInvoiceTotals = () => {
|
|
||||||
const {
|
|
||||||
values: { entries, currency_code: currencyCode },
|
|
||||||
} = useFormikContext();
|
|
||||||
|
|
||||||
// Retrieves the invoice entries total.
|
|
||||||
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
|
||||||
|
|
||||||
const total_ = useInvoiceTotal();
|
|
||||||
|
|
||||||
// Retrieves the formatted total money.
|
|
||||||
const formattedTotal = React.useMemo(
|
|
||||||
() => formattedAmount(total_, currencyCode),
|
|
||||||
[total_, currencyCode],
|
|
||||||
);
|
|
||||||
// Retrieves the formatted subtotal.
|
|
||||||
const formattedSubtotal = React.useMemo(
|
|
||||||
() => formattedAmount(total, currencyCode, { money: false }),
|
|
||||||
[total, currencyCode],
|
|
||||||
);
|
|
||||||
// Retrieves the payment total.
|
|
||||||
const paymentTotal = React.useMemo(() => 0, []);
|
|
||||||
|
|
||||||
// Retireves the formatted payment total.
|
|
||||||
const formattedPaymentTotal = React.useMemo(
|
|
||||||
() => formattedAmount(paymentTotal, currencyCode),
|
|
||||||
[paymentTotal, currencyCode],
|
|
||||||
);
|
|
||||||
// Retrieves the formatted due total.
|
|
||||||
const dueTotal = React.useMemo(
|
|
||||||
() => total - paymentTotal,
|
|
||||||
[total, paymentTotal],
|
|
||||||
);
|
|
||||||
// Retrieves the formatted due total.
|
|
||||||
const formattedDueTotal = React.useMemo(
|
|
||||||
() => formattedAmount(dueTotal, currencyCode),
|
|
||||||
[dueTotal, currencyCode],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
total,
|
|
||||||
paymentTotal,
|
|
||||||
dueTotal,
|
|
||||||
formattedTotal,
|
|
||||||
formattedSubtotal,
|
|
||||||
formattedPaymentTotal,
|
|
||||||
formattedDueTotal,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the invoice has foreign customer.
|
* Detarmines whether the invoice has foreign customer.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@@ -409,14 +356,25 @@ export const useInvoiceTotal = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the paid amount of the invoice.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const useInvoicePaidAmount = () => {
|
||||||
|
const { invoice } = useInvoiceFormContext();
|
||||||
|
|
||||||
|
return invoice?.payment_amount || 0;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreives the invoice due amount.
|
* Retreives the invoice due amount.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
export const useInvoiceDueAmount = () => {
|
export const useInvoiceDueAmount = () => {
|
||||||
const total = useInvoiceTotal();
|
const total = useInvoiceTotal();
|
||||||
|
const paidAmount = useInvoicePaidAmount();
|
||||||
|
|
||||||
return total;
|
return Math.max(total - paidAmount, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -438,3 +396,13 @@ export const useIsInvoiceTaxExclusive = () => {
|
|||||||
|
|
||||||
return values.inclusive_exclusive_tax === TaxType.Exclusive;
|
return values.inclusive_exclusive_tax === TaxType.Exclusive;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the invoice currency code.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const useInvoiceCurrencyCode = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
return values.currency_code;
|
||||||
|
};
|
||||||
|
|||||||
@@ -276,6 +276,11 @@ const onValidateExcludeUncategorizedTransaction = (queryClient) => {
|
|||||||
|
|
||||||
// invalidate bank account summary.
|
// invalidate bank account summary.
|
||||||
queryClient.invalidateQueries(BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
queryClient.invalidateQueries(BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
|
|
||||||
|
// Invalidate the recognized transactions.
|
||||||
|
queryClient.invalidateQueries([
|
||||||
|
BANK_QUERY_KEY.RECOGNIZED_BANK_TRANSACTIONS_INFINITY,
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
type ExcludeUncategorizedTransactionValue = number;
|
type ExcludeUncategorizedTransactionValue = number;
|
||||||
@@ -312,10 +317,6 @@ export function useExcludeUncategorizedTransaction(
|
|||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
onValidateExcludeUncategorizedTransaction(queryClient);
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
queryClient.invalidateQueries([
|
|
||||||
BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META,
|
|
||||||
id,
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
@@ -357,10 +358,6 @@ export function useUnexcludeUncategorizedTransaction(
|
|||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
onValidateExcludeUncategorizedTransaction(queryClient);
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
queryClient.invalidateQueries([
|
|
||||||
BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META,
|
|
||||||
id,
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
@@ -649,7 +646,6 @@ export function useRecognizedBankTransactionsInfinity(
|
|||||||
getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
|
getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
|
||||||
getNextPageParam: (lastPage) => {
|
getNextPageParam: (lastPage) => {
|
||||||
const { pagination } = lastPage;
|
const { pagination } = lastPage;
|
||||||
|
|
||||||
return pagination.total > pagination.page_size * pagination.page
|
return pagination.total > pagination.page_size * pagination.page
|
||||||
? lastPage.pagination.page + 1
|
? lastPage.pagination.page + 1
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user