mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
feat: excluded bank transactions
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { param } from 'express-validator';
|
import { param } from 'express-validator';
|
||||||
import { NextFunction, Request, Response, Router } from 'express';
|
import { NextFunction, Request, Response, Router, query } from 'express';
|
||||||
import BaseController from '../BaseController';
|
import BaseController from '../BaseController';
|
||||||
import { ExcludeBankTransactionsApplication } from '@/services/Banking/Exclude/ExcludeBankTransactionsApplication';
|
import { ExcludeBankTransactionsApplication } from '@/services/Banking/Exclude/ExcludeBankTransactionsApplication';
|
||||||
|
|
||||||
@@ -27,6 +27,12 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.unexcludeBankTransaction.bind(this)
|
this.unexcludeBankTransaction.bind(this)
|
||||||
);
|
);
|
||||||
|
router.get(
|
||||||
|
'/excluded',
|
||||||
|
[],
|
||||||
|
this.validationResult,
|
||||||
|
this.getExcludedBankTransactions.bind(this)
|
||||||
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,4 +93,32 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the excluded uncategorized bank transactions.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
* @returns {Promise<Response|null>}
|
||||||
|
*/
|
||||||
|
private async getExcludedBankTransactions(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<Response | void> {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const filter = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
console.log('123');
|
||||||
|
try {
|
||||||
|
const data =
|
||||||
|
await this.excludeBankTransactionApp.getExcludedBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
return res.status(200).send(data);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,11 @@ export class RecognizedTransactionsController extends BaseController {
|
|||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get(
|
router.get('/', this.getRecognizedTransactions.bind(this));
|
||||||
'/accounts/:accountId',
|
|
||||||
this.getRecognizedTransactions.bind(this)
|
|
||||||
);
|
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
k;
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the recognized bank transactions.
|
* Retrieves the recognized bank transactions.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
@@ -34,15 +31,15 @@ export class RecognizedTransactionsController extends BaseController {
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
) {
|
) {
|
||||||
const { accountId } = req.params;
|
const filter = this.matchedQueryData(req);
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.cashflowApplication.getRecognizedTransactions(
|
const data = await this.cashflowApplication.getRecognizedTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
accountId
|
filter
|
||||||
);
|
);
|
||||||
return res.status(200).send({ data });
|
return res.status(200).send(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,3 +164,10 @@ export interface IGetUncategorizedTransactionsQuery {
|
|||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface IGetRecognizedTransactionsQuery {
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
accountId?: number;
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ export class RecognizedBankTransaction extends TenantModel {
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const UncategorizedCashflowTransaction = require('./UncategorizedCashflowTransaction');
|
const UncategorizedCashflowTransaction = require('./UncategorizedCashflowTransaction');
|
||||||
const Account = require('./Account');
|
const Account = require('./Account');
|
||||||
|
const { BankRule } = require('./BankRule');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@@ -54,6 +55,18 @@ export class RecognizedBankTransaction extends TenantModel {
|
|||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recognized bank transaction may belongs to bank rule.
|
||||||
|
*/
|
||||||
|
bankRule: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: BankRule,
|
||||||
|
join: {
|
||||||
|
from: 'recognized_bank_transactions.bankRuleId',
|
||||||
|
to: 'bank_rules.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { ExcludeBankTransaction } from './ExcludeBankTransaction';
|
import { ExcludeBankTransaction } from './ExcludeBankTransaction';
|
||||||
import { UnexcludeBankTransaction } from './UnexcludeBankTransaction';
|
import { UnexcludeBankTransaction } from './UnexcludeBankTransaction';
|
||||||
|
import { GetExcludedBankTransactionsService } from './GetExcludedBankTransactions';
|
||||||
|
import { ExcludedBankTransactionsQuery } from './_types';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExcludeBankTransactionsApplication {
|
export class ExcludeBankTransactionsApplication {
|
||||||
@@ -10,6 +12,9 @@ export class ExcludeBankTransactionsApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private unexcludeBankTransactionService: UnexcludeBankTransaction;
|
private unexcludeBankTransactionService: UnexcludeBankTransaction;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private getExcludedBankTransactionsService: GetExcludedBankTransactionsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a bank transaction as excluded.
|
* Marks a bank transaction as excluded.
|
||||||
* @param {number} tenantId - The ID of the tenant.
|
* @param {number} tenantId - The ID of the tenant.
|
||||||
@@ -35,4 +40,20 @@ export class ExcludeBankTransactionsApplication {
|
|||||||
bankTransactionId
|
bankTransactionId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the excluded bank transactions.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ExcludedBankTransactionsQuery} filter
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public getExcludedBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ExcludedBankTransactionsQuery
|
||||||
|
) {
|
||||||
|
return this.getExcludedBankTransactionsService.getExcludedBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { ExcludedBankTransactionsQuery } from './_types';
|
||||||
|
import { UncategorizedTransactionTransformer } from '@/services/Cashflow/UncategorizedTransactionTransformer';
|
||||||
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class GetExcludedBankTransactionsService {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the excluded uncategorized bank transactions.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ExcludedBankTransactionsQuery} filter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getExcludedBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ExcludedBankTransactionsQuery
|
||||||
|
) {
|
||||||
|
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Parsed query with default values.
|
||||||
|
const _query = {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
...filter,
|
||||||
|
};
|
||||||
|
const { results, pagination } =
|
||||||
|
await UncategorizedCashflowTransaction.query()
|
||||||
|
.onBuild((q) => {
|
||||||
|
q.where('excluded', true);
|
||||||
|
q.orderBy('date', 'DESC');
|
||||||
|
|
||||||
|
if (_query.accountId) {
|
||||||
|
q.where('account_id', _query.accountId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.pagination(_query.page - 1, _query.pageSize);
|
||||||
|
|
||||||
|
const data = await this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
results,
|
||||||
|
new UncategorizedTransactionTransformer()
|
||||||
|
);
|
||||||
|
return { data, pagination };
|
||||||
|
}
|
||||||
|
}
|
||||||
6
packages/server/src/services/Banking/Exclude/_types.ts
Normal file
6
packages/server/src/services/Banking/Exclude/_types.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export interface ExcludedBankTransactionsQuery {
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
accountId?: number;
|
||||||
|
}
|
||||||
@@ -65,7 +65,6 @@ export class RecognizeTranasctionsService {
|
|||||||
|
|
||||||
if (batch) query.where('batch', batch);
|
if (batch) query.where('batch', batch);
|
||||||
});
|
});
|
||||||
|
|
||||||
const bankRules = await BankRule.query().withGraphFetched('conditions');
|
const bankRules = await BankRule.query().withGraphFetched('conditions');
|
||||||
const bankRulesByAccountId = transformToMapBy(
|
const bankRulesByAccountId = transformToMapBy(
|
||||||
bankRules,
|
bankRules,
|
||||||
@@ -92,7 +91,7 @@ export class RecognizeTranasctionsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await PromisePool.withConcurrency(MIGRATION_CONCURRENCY)
|
const result = await PromisePool.withConcurrency(MIGRATION_CONCURRENCY)
|
||||||
.for(uncategorizedTranasctions)
|
.for(uncategorizedTranasctions)
|
||||||
.process((transaction: UncategorizedCashflowTransaction, index, pool) => {
|
.process((transaction: UncategorizedCashflowTransaction, index, pool) => {
|
||||||
return regonizeTransaction(transaction);
|
return regonizeTransaction(transaction);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
ICashflowAccountsFilter,
|
ICashflowAccountsFilter,
|
||||||
ICashflowNewCommandDTO,
|
ICashflowNewCommandDTO,
|
||||||
ICategorizeCashflowTransactioDTO,
|
ICategorizeCashflowTransactioDTO,
|
||||||
|
IGetRecognizedTransactionsQuery,
|
||||||
IGetUncategorizedTransactionsQuery,
|
IGetUncategorizedTransactionsQuery,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { CategorizeTransactionAsExpense } from './CategorizeTransactionAsExpense';
|
import { CategorizeTransactionAsExpense } from './CategorizeTransactionAsExpense';
|
||||||
@@ -18,6 +19,7 @@ import { GetUncategorizedTransaction } from './GetUncategorizedTransaction';
|
|||||||
import NewCashflowTransactionService from './NewCashflowTransactionService';
|
import NewCashflowTransactionService from './NewCashflowTransactionService';
|
||||||
import GetCashflowAccountsService from './GetCashflowAccountsService';
|
import GetCashflowAccountsService from './GetCashflowAccountsService';
|
||||||
import { GetCashflowTransactionService } from './GetCashflowTransactionsService';
|
import { GetCashflowTransactionService } from './GetCashflowTransactionsService';
|
||||||
|
import { GetRecognizedTransactionsService } from './GetRecongizedTransactions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class CashflowApplication {
|
export class CashflowApplication {
|
||||||
@@ -51,6 +53,9 @@ export class CashflowApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private createUncategorizedTransactionService: CreateUncategorizedTransaction;
|
private createUncategorizedTransactionService: CreateUncategorizedTransaction;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private getRecognizedTranasctionsService: GetRecognizedTransactionsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cashflow transaction.
|
* Creates a new cashflow transaction.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -213,4 +218,20 @@ export class CashflowApplication {
|
|||||||
uncategorizedTransactionId
|
uncategorizedTransactionId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the recognized bank transactions.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} accountId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public getRecognizedTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
filter?: IGetRecognizedTransactionsQuery
|
||||||
|
) {
|
||||||
|
return this.getRecognizedTranasctionsService.getRecognizedTranactions(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
|
import { formatNumber } from '@/utils';
|
||||||
|
|
||||||
|
export class GetRecognizedTransactionTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale credit note object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'uncategorizedTransactionId',
|
||||||
|
'referenceNo',
|
||||||
|
'description',
|
||||||
|
'payee',
|
||||||
|
'amount',
|
||||||
|
'formattedAmount',
|
||||||
|
'date',
|
||||||
|
'formattedDate',
|
||||||
|
'assignedAccountId',
|
||||||
|
'assignedAccountName',
|
||||||
|
'assignedAccountCode',
|
||||||
|
'assignedPayee',
|
||||||
|
'assignedMemo',
|
||||||
|
'assignedCategory',
|
||||||
|
'assignedCategoryFormatted',
|
||||||
|
'withdrawal',
|
||||||
|
'deposit',
|
||||||
|
'isDepositTransaction',
|
||||||
|
'isWithdrawalTransaction',
|
||||||
|
'formattedDepositAmount',
|
||||||
|
'formattedWithdrawalAmount',
|
||||||
|
'bankRuleId',
|
||||||
|
'bankRuleName',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude all attributes.
|
||||||
|
* @returns {Array<string>}
|
||||||
|
*/
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['*'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the uncategorized transaction id.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
public uncategorizedTransactionId = (transaction): number => {
|
||||||
|
return transaction.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference number of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public referenceNo(transaction: any): string {
|
||||||
|
return transaction.referenceNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the description of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public description(transaction: any): string {
|
||||||
|
return transaction.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the payee of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public payee(transaction: any): string {
|
||||||
|
return transaction.payee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the amount of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
public amount(transaction: any): number {
|
||||||
|
return transaction.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the formatted amount of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public formattedAmount(transaction: any): string {
|
||||||
|
return this.formatNumber(transaction.formattedAmount, {
|
||||||
|
money: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the date of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public date(transaction: any): string {
|
||||||
|
return transaction.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the formatted date of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public formattedDate(transaction: any): string {
|
||||||
|
return this.formatDate(transaction.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned account ID of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
public assignedAccountId(transaction: any): number {
|
||||||
|
return transaction.recognizedTransaction.assignedAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned account name of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public assignedAccountName(transaction: any): string {
|
||||||
|
return transaction.recognizedTransaction.assignAccount.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned account code of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public assignedAccountCode(transaction: any): string {
|
||||||
|
return transaction.recognizedTransaction.assignAccount.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned payee of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public getAssignedPayee(transaction: any): string {
|
||||||
|
return transaction.recognizedTransaction.assignedPayee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned memo of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public assignedMemo(transaction: any): string {
|
||||||
|
return transaction.recognizedTransaction.assignedMemo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned category of the transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public assignedCategory(transaction: any): string {
|
||||||
|
return transaction.recognizedTransaction.assignedCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public assignedCategoryFormatted() {
|
||||||
|
return 'Other Income'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the transaction is a withdrawal.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
public isWithdrawal(transaction: any): boolean {
|
||||||
|
return transaction.withdrawal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the transaction is a deposit.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
public isDeposit(transaction: any): boolean {
|
||||||
|
return transaction.deposit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the transaction is a deposit transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
public isDepositTransaction(transaction: any): boolean {
|
||||||
|
return transaction.isDepositTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the transaction is a withdrawal transaction.
|
||||||
|
* @param {object} transaction
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
public isWithdrawalTransaction(transaction: any): boolean {
|
||||||
|
return transaction.isWithdrawalTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted deposit amount.
|
||||||
|
* @param {any} transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDepositAmount(transaction) {
|
||||||
|
if (transaction.isDepositTransaction) {
|
||||||
|
return formatNumber(transaction.deposit, {
|
||||||
|
currencyCode: transaction.currencyCode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted withdrawal amount.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedWithdrawalAmount(transaction) {
|
||||||
|
if (transaction.isWithdrawalTransaction) {
|
||||||
|
return formatNumber(transaction.withdrawal, {
|
||||||
|
currencyCode: transaction.currencyCode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transaction bank rule id.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected bankRuleId(transaction) {
|
||||||
|
return transaction.recognizedTransaction.bankRuleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transaction bank rule name.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected bankRuleName(transaction) {
|
||||||
|
return transaction.recognizedTransaction.bankRule.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
import { GetRecognizedTransactionTransformer } from './GetRecognizedTransactionTransformer';
|
||||||
|
import { IGetRecognizedTransactionsQuery } from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class GetRecognizedTransactionsService {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the recognized transactions of the given account.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IGetRecognizedTransactionsQuery} filter -
|
||||||
|
*/
|
||||||
|
async getRecognizedTranactions(
|
||||||
|
tenantId: number,
|
||||||
|
filter?: IGetRecognizedTransactionsQuery
|
||||||
|
) {
|
||||||
|
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const _filter = {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
...filter,
|
||||||
|
};
|
||||||
|
const { results, pagination } =
|
||||||
|
await UncategorizedCashflowTransaction.query()
|
||||||
|
.onBuild((q) => {
|
||||||
|
q.withGraphFetched('recognizedTransaction.assignAccount');
|
||||||
|
q.withGraphFetched('recognizedTransaction.bankRule');
|
||||||
|
q.whereNotNull('recognizedTransactionId');
|
||||||
|
|
||||||
|
if (_filter.accountId) {
|
||||||
|
q.where('accountId', _filter.accountId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.pagination(_filter.page - 1, _filter.pageSize);
|
||||||
|
|
||||||
|
const data = await this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
results,
|
||||||
|
new GetRecognizedTransactionTransformer()
|
||||||
|
);
|
||||||
|
return { data, pagination };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ export class UncategorizedTransactionTransformer extends Transformer {
|
|||||||
return [
|
return [
|
||||||
'formattedAmount',
|
'formattedAmount',
|
||||||
'formattedDate',
|
'formattedDate',
|
||||||
'formattetDepositAmount',
|
'formattedDepositAmount',
|
||||||
'formattedWithdrawalAmount',
|
'formattedWithdrawalAmount',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -40,7 +40,7 @@ export class UncategorizedTransactionTransformer extends Transformer {
|
|||||||
* @param transaction
|
* @param transaction
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
protected formattetDepositAmount(transaction) {
|
protected formattedDepositAmount(transaction) {
|
||||||
if (transaction.isDepositTransaction) {
|
if (transaction.isDepositTransaction) {
|
||||||
return formatNumber(transaction.deposit, {
|
return formatNumber(transaction.deposit, {
|
||||||
currencyCode: transaction.currencyCode,
|
currencyCode: transaction.currencyCode,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const TABLES = {
|
|||||||
PROJECTS: 'projects',
|
PROJECTS: 'projects',
|
||||||
TIMESHEETS: 'timesheets',
|
TIMESHEETS: 'timesheets',
|
||||||
PROJECT_TASKS: 'project_tasks',
|
PROJECT_TASKS: 'project_tasks',
|
||||||
|
UNCATEGORIZED_ACCOUNT_TRANSACTIONS: 'UNCATEGORIZED_ACCOUNT_TRANSACTIONS',
|
||||||
|
EXCLUDED_BANK_TRANSACTIONS: 'EXCLUDED_BANK_TRANSACTIONS'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TABLE_SIZE = {
|
export const TABLE_SIZE = {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export function AccountTransactionsUncategorizeFilter() {
|
|||||||
<FilterTag round interactive>
|
<FilterTag round interactive>
|
||||||
All <strong>(2)</strong>
|
All <strong>(2)</strong>
|
||||||
</FilterTag>
|
</FilterTag>
|
||||||
|
|
||||||
<FilterTag round minimal interactive>
|
<FilterTag round minimal interactive>
|
||||||
Recognized <strong>(0)</strong>
|
Recognized <strong>(0)</strong>
|
||||||
</FilterTag>
|
</FilterTag>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface UncategorizedTransactionsFilterValue {}
|
||||||
|
|
||||||
|
const UncategorizedTransactionsFilterContext =
|
||||||
|
React.createContext<UncategorizedTransactionsFilterValue>(
|
||||||
|
{} as UncategorizedTransactionsFilterValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface UncategorizedTransactionsFilterProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function UncategorizedTransactionsFilterProvider({
|
||||||
|
...props
|
||||||
|
}: UncategorizedTransactionsFilterProviderProps) {
|
||||||
|
// Provider payload.
|
||||||
|
const provider = {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UncategorizedTransactionsFilterContext.Provider
|
||||||
|
value={provider}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useUncategorizedTransactionsFilter = () =>
|
||||||
|
React.useContext(UncategorizedTransactionsFilterContext);
|
||||||
|
|
||||||
|
export {
|
||||||
|
UncategorizedTransactionsFilterProvider,
|
||||||
|
useUncategorizedTransactionsFilter,
|
||||||
|
};
|
||||||
@@ -4,7 +4,6 @@ import styled from 'styled-components';
|
|||||||
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
|
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
|
||||||
|
|
||||||
import AccountTransactionsDataTable from './AccountTransactionsDataTable';
|
import AccountTransactionsDataTable from './AccountTransactionsDataTable';
|
||||||
import { AccountTransactionsUncategorizeFilter } from './AccountTransactionsUncategorizeFilter';
|
|
||||||
import { AccountTransactionsAllProvider } from './AccountTransactionsAllBoot';
|
import { AccountTransactionsAllProvider } from './AccountTransactionsAllBoot';
|
||||||
|
|
||||||
const Box = styled.div`
|
const Box = styled.div`
|
||||||
@@ -23,8 +22,6 @@ export default function AccountTransactionsAll() {
|
|||||||
return (
|
return (
|
||||||
<AccountTransactionsAllProvider>
|
<AccountTransactionsAllProvider>
|
||||||
<Box>
|
<Box>
|
||||||
<AccountTransactionsUncategorizeFilter />
|
|
||||||
|
|
||||||
<CashflowTransactionsTableCard>
|
<CashflowTransactionsTableCard>
|
||||||
<AccountTransactionsDataTable />
|
<AccountTransactionsDataTable />
|
||||||
</CashflowTransactionsTableCard>
|
</CashflowTransactionsTableCard>
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
import { useEffect } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
|
||||||
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
|
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
|
||||||
|
|
||||||
import AccountTransactionsUncategorizedTable from './AccountTransactionsUncategorizedTable';
|
import { AccountTransactionsUncategorizeFilter } from './AccountTransactionsUncategorizeFilter';
|
||||||
import { AccountUncategorizedTransactionsBoot } from './AllTransactionsUncategorizedBoot';
|
import { UncategorizedTransactionsFilterProvider } from './AccountUncategorizedTransactionsFilterProvider';
|
||||||
|
import { RecognizedTransactionsTableBoot } from './RecognizedTransactions/RecognizedTransactionsTableBoot';
|
||||||
|
import { RecognizedTransactionsTable } from './RecognizedTransactions/RecognizedTransactionsTable';
|
||||||
import {
|
import {
|
||||||
WithBankingActionsProps,
|
WithBankingActionsProps,
|
||||||
withBankingActions,
|
withBankingActions,
|
||||||
} from '../withBankingActions';
|
} from '../withBankingActions';
|
||||||
import { useEffect } from 'react';
|
import { AccountUncategorizedTransactionsBoot } from './AllTransactionsUncategorizedBoot';
|
||||||
|
import AccountTransactionsUncategorizedTable from './AccountTransactionsUncategorizedTable';
|
||||||
|
import { ExcludedBankTransactionsTableBoot } from './ExcludedTransactions/ExcludedTransactionsTableBoot';
|
||||||
|
import { ExcludedTransactionsTable } from './ExcludedTransactions/ExcludedTransactionsTable';
|
||||||
|
|
||||||
const Box = styled.div`
|
const Box = styled.div`
|
||||||
margin: 30px 15px;
|
margin: 30px 15px;
|
||||||
@@ -36,14 +42,31 @@ function AllTransactionsUncategorizedRoot({
|
|||||||
},
|
},
|
||||||
[closeMatchingTransactionAside],
|
[closeMatchingTransactionAside],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccountUncategorizedTransactionsBoot>
|
<UncategorizedTransactionsFilterProvider>
|
||||||
<Box>
|
<Box>
|
||||||
<CashflowTransactionsTableCard>
|
<AccountTransactionsUncategorizeFilter />
|
||||||
<AccountTransactionsUncategorizedTable />
|
|
||||||
</CashflowTransactionsTableCard>
|
<ExcludedBankTransactionsTableBoot>
|
||||||
|
<CashflowTransactionsTableCard>
|
||||||
|
<ExcludedTransactionsTable />
|
||||||
|
</CashflowTransactionsTableCard>
|
||||||
|
</ExcludedBankTransactionsTableBoot>
|
||||||
|
|
||||||
|
{/* <RecognizedTransactionsTableBoot>
|
||||||
|
<CashflowTransactionsTableCard>
|
||||||
|
<RecognizedTransactionsTable />
|
||||||
|
</CashflowTransactionsTableCard>
|
||||||
|
</RecognizedTransactionsTableBoot> */}
|
||||||
|
|
||||||
|
{/* <AccountUncategorizedTransactionsBoot>
|
||||||
|
<CashflowTransactionsTableCard>
|
||||||
|
<AccountTransactionsUncategorizedTable />
|
||||||
|
</CashflowTransactionsTableCard>
|
||||||
|
</AccountUncategorizedTransactionsBoot> */}
|
||||||
</Box>
|
</Box>
|
||||||
</AccountUncategorizedTransactionsBoot>
|
</UncategorizedTransactionsFilterProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataTable,
|
||||||
|
TableFastCell,
|
||||||
|
TableSkeletonRows,
|
||||||
|
TableSkeletonHeader,
|
||||||
|
TableVirtualizedListRows,
|
||||||
|
AppToaster,
|
||||||
|
} from '@/components';
|
||||||
|
import { TABLES } from '@/constants/tables';
|
||||||
|
|
||||||
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
|
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||||
|
import { useExcludedTransactionsColumns } from './_utils';
|
||||||
|
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||||
|
|
||||||
|
import { compose } from '@/utils';
|
||||||
|
import { ActionsMenu } from './_components';
|
||||||
|
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
interface ExcludedTransactionsTableProps {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the recognized account transactions datatable.
|
||||||
|
*/
|
||||||
|
function ExcludedTransactionsTableRoot({}: ExcludedTransactionsTableProps) {
|
||||||
|
const { excludedBankTransactions } = useExcludedTransactionsBoot();
|
||||||
|
const { mutateAsync: unexcludeBankTransaction } =
|
||||||
|
useUnexcludeUncategorizedTransaction();
|
||||||
|
|
||||||
|
// Retrieve table columns.
|
||||||
|
const columns = useExcludedTransactionsColumns();
|
||||||
|
|
||||||
|
// Local storage memorizing columns widths.
|
||||||
|
const [initialColumnsWidths, , handleColumnResizing] =
|
||||||
|
useMemorizedColumnsWidths(TABLES.UNCATEGORIZED_ACCOUNT_TRANSACTIONS);
|
||||||
|
|
||||||
|
// Handle cell click.
|
||||||
|
const handleCellClick = (cell, event) => {};
|
||||||
|
|
||||||
|
// Handle restore button click.
|
||||||
|
const handleRestoreClick = (transaction) => {
|
||||||
|
unexcludeBankTransaction(transaction.id)
|
||||||
|
.then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'The excluded bank transaction has been restored.',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CashflowTransactionsTable
|
||||||
|
noInitialFetch={true}
|
||||||
|
columns={columns}
|
||||||
|
data={excludedBankTransactions}
|
||||||
|
sticky={true}
|
||||||
|
loading={false}
|
||||||
|
headerLoading={false}
|
||||||
|
expandColumnSpace={1}
|
||||||
|
expandToggleColumn={2}
|
||||||
|
selectionColumnWidth={45}
|
||||||
|
TableCellRenderer={TableFastCell}
|
||||||
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
|
TableRowsRenderer={TableVirtualizedListRows}
|
||||||
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
ContextMenu={ActionsMenu}
|
||||||
|
onCellClick={handleCellClick}
|
||||||
|
// #TableVirtualizedListRows props.
|
||||||
|
vListrowHeight={'small' == 'small' ? 32 : 40}
|
||||||
|
vListrowHeight={40}
|
||||||
|
vListOverscanRowCount={0}
|
||||||
|
initialColumnsWidths={initialColumnsWidths}
|
||||||
|
onColumnResizing={handleColumnResizing}
|
||||||
|
// noResults={<T id={'cash_flow.account_transactions.no_results'} />}
|
||||||
|
className="table-constrant"
|
||||||
|
payload={{
|
||||||
|
onRestore: handleRestoreClick,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ExcludedTransactionsTable = compose(withDrawerActions)(
|
||||||
|
ExcludedTransactionsTableRoot,
|
||||||
|
);
|
||||||
|
|
||||||
|
const DashboardConstrantTable = styled(DataTable)`
|
||||||
|
.table {
|
||||||
|
.thead {
|
||||||
|
.th {
|
||||||
|
background: #fff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody {
|
||||||
|
.tr:last-child .td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||||
|
.table .tbody {
|
||||||
|
.tbody-inner .tr.no-results {
|
||||||
|
.td {
|
||||||
|
padding: 2rem 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody-inner {
|
||||||
|
.tr .td {
|
||||||
|
border-bottom: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { flatten, map } from 'lodash';
|
||||||
|
import { IntersectionObserver } from '@/components';
|
||||||
|
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||||
|
import { useExcludedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
||||||
|
|
||||||
|
interface ExcludedBankTransactionsContextValue {
|
||||||
|
isExcludedTransactionsLoading: boolean;
|
||||||
|
isExcludedTransactionsFetching: boolean;
|
||||||
|
excludedBankTransactions: Array<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExcludedTransactionsContext =
|
||||||
|
React.createContext<ExcludedBankTransactionsContextValue>(
|
||||||
|
{} as ExcludedBankTransactionsContextValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
function flattenInfinityPagesData(data) {
|
||||||
|
return flatten(map(data.pages, (page) => page.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExcludedBankTransactionsTableBootProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account uncategorized transctions provider.
|
||||||
|
*/
|
||||||
|
function ExcludedBankTransactionsTableBoot({
|
||||||
|
children,
|
||||||
|
}: ExcludedBankTransactionsTableBootProps) {
|
||||||
|
const { accountId } = useAccountTransactionsContext();
|
||||||
|
|
||||||
|
// Fetches the uncategorized transactions.
|
||||||
|
const {
|
||||||
|
data: recognizedTransactionsPage,
|
||||||
|
isFetching: isExcludedTransactionsFetching,
|
||||||
|
isLoading: isExcludedTransactionsLoading,
|
||||||
|
isSuccess: isRecognizedTransactionsSuccess,
|
||||||
|
isFetchingNextPage: isUncategorizedTransactionFetchNextPage,
|
||||||
|
fetchNextPage: fetchNextrecognizedTransactionsPage,
|
||||||
|
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||||
|
} = useExcludedBankTransactionsInfinity({
|
||||||
|
page_size: 50,
|
||||||
|
account_id: accountId,
|
||||||
|
});
|
||||||
|
// Memorized the cashflow account transactions.
|
||||||
|
const excludedBankTransactions = React.useMemo(
|
||||||
|
() =>
|
||||||
|
isRecognizedTransactionsSuccess
|
||||||
|
? flattenInfinityPagesData(recognizedTransactionsPage)
|
||||||
|
: [],
|
||||||
|
[recognizedTransactionsPage, isRecognizedTransactionsSuccess],
|
||||||
|
);
|
||||||
|
// Handle the observer ineraction.
|
||||||
|
const handleObserverInteract = React.useCallback(() => {
|
||||||
|
if (
|
||||||
|
!isExcludedTransactionsFetching &&
|
||||||
|
hasUncategorizedTransactionsNextPage
|
||||||
|
) {
|
||||||
|
fetchNextrecognizedTransactionsPage();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isExcludedTransactionsFetching,
|
||||||
|
hasUncategorizedTransactionsNextPage,
|
||||||
|
fetchNextrecognizedTransactionsPage,
|
||||||
|
]);
|
||||||
|
// Provider payload.
|
||||||
|
const provider = {
|
||||||
|
excludedBankTransactions,
|
||||||
|
isExcludedTransactionsFetching,
|
||||||
|
isExcludedTransactionsLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExcludedTransactionsContext.Provider value={provider}>
|
||||||
|
{children}
|
||||||
|
<IntersectionObserver
|
||||||
|
onIntersect={handleObserverInteract}
|
||||||
|
enabled={!isUncategorizedTransactionFetchNextPage}
|
||||||
|
/>
|
||||||
|
</ExcludedTransactionsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useExcludedTransactionsBoot = () =>
|
||||||
|
React.useContext(ExcludedTransactionsContext);
|
||||||
|
|
||||||
|
export { ExcludedBankTransactionsTableBoot, useExcludedTransactionsBoot };
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||||
|
import { safeCallback } from '@/utils';
|
||||||
|
|
||||||
|
export function ActionsMenu({ payload: { onRestore }, row: { original } }) {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem text={'Restore'} onClick={safeCallback(onRestore, original)} />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { getColumnWidth } from '@/utils';
|
||||||
|
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||||
|
|
||||||
|
const getReportColWidth = (data, accessor, headerText) => {
|
||||||
|
return getColumnWidth(
|
||||||
|
data,
|
||||||
|
accessor,
|
||||||
|
{ magicSpacing: 10, minWidth: 100 },
|
||||||
|
headerText,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const descriptionAccessor = (transaction) => {
|
||||||
|
return <span style={{ color: '#5F6B7C' }}>{transaction.description}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve excluded transactions columns table.
|
||||||
|
*/
|
||||||
|
export function useExcludedTransactionsColumns() {
|
||||||
|
const { excludedBankTransactions: data } = useExcludedTransactionsBoot();
|
||||||
|
|
||||||
|
const withdrawalWidth = getReportColWidth(
|
||||||
|
data,
|
||||||
|
'formatted_withdrawal_amount',
|
||||||
|
'Withdrawal',
|
||||||
|
);
|
||||||
|
const depositWidth = getReportColWidth(
|
||||||
|
data,
|
||||||
|
'formatted_deposit_amount',
|
||||||
|
'Deposit',
|
||||||
|
);
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Date',
|
||||||
|
accessor: 'formatted_date',
|
||||||
|
width: 110,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Description',
|
||||||
|
accessor: descriptionAccessor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Payee',
|
||||||
|
accessor: 'payee',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Deposit',
|
||||||
|
accessor: 'formatted_deposit_amount',
|
||||||
|
align: 'right',
|
||||||
|
width: depositWidth,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Withdrawal',
|
||||||
|
accessor: 'formatted_withdrawal_amount',
|
||||||
|
align: 'right',
|
||||||
|
width: withdrawalWidth,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataTable,
|
||||||
|
TableFastCell,
|
||||||
|
TableSkeletonRows,
|
||||||
|
TableSkeletonHeader,
|
||||||
|
TableVirtualizedListRows,
|
||||||
|
FormattedMessage as T,
|
||||||
|
AppToaster,
|
||||||
|
} from '@/components';
|
||||||
|
import { TABLES } from '@/constants/tables';
|
||||||
|
|
||||||
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
|
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||||
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
|
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||||
|
import { useUncategorizedTransactionsColumns } from './_utils';
|
||||||
|
import { useRecognizedTransactionsBoot } from './RecognizedTransactionsTableBoot';
|
||||||
|
|
||||||
|
import { ActionsMenu } from './_components';
|
||||||
|
import { compose } from '@/utils';
|
||||||
|
import { useExcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
import {
|
||||||
|
WithBankingActionsProps,
|
||||||
|
withBankingActions,
|
||||||
|
} from '../../withBankingActions';
|
||||||
|
|
||||||
|
interface RecognizedTransactionsTableProps extends WithBankingActionsProps {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the recognized account transactions datatable.
|
||||||
|
*/
|
||||||
|
function RecognizedTransactionsTableRoot({
|
||||||
|
// #withSettings
|
||||||
|
cashflowTansactionsTableSize,
|
||||||
|
|
||||||
|
// #withAlertsActions
|
||||||
|
openAlert,
|
||||||
|
|
||||||
|
// #withDrawerActions
|
||||||
|
openDrawer,
|
||||||
|
|
||||||
|
// #withBanking
|
||||||
|
setUncategorizedTransactionIdForMatching,
|
||||||
|
}: RecognizedTransactionsTableProps) {
|
||||||
|
const { mutateAsync: excludeBankTransaction } =
|
||||||
|
useExcludeUncategorizedTransaction();
|
||||||
|
|
||||||
|
const { recognizedTransactions } = useRecognizedTransactionsBoot();
|
||||||
|
|
||||||
|
// Retrieve table columns.
|
||||||
|
const columns = useUncategorizedTransactionsColumns();
|
||||||
|
|
||||||
|
// Local storage memorizing columns widths.
|
||||||
|
const [initialColumnsWidths, , handleColumnResizing] =
|
||||||
|
useMemorizedColumnsWidths(TABLES.UNCATEGORIZED_ACCOUNT_TRANSACTIONS);
|
||||||
|
|
||||||
|
// Handle cell click.
|
||||||
|
const handleCellClick = (cell, event) => {
|
||||||
|
setUncategorizedTransactionIdForMatching(
|
||||||
|
cell.row.original.uncategorized_transaction_id,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Handle exclude button click.
|
||||||
|
const handleExcludeClick = (transaction) => {
|
||||||
|
excludeBankTransaction(transaction.uncategorized_transaction_id)
|
||||||
|
.then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
message: 'The bank transaction has been excluded.',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
message: 'Something went wrong.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
const handleCategorizeClick = (transaction) => {
|
||||||
|
setUncategorizedTransactionIdForMatching(
|
||||||
|
transaction.uncategorized_transaction_id,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CashflowTransactionsTable
|
||||||
|
noInitialFetch={true}
|
||||||
|
columns={columns}
|
||||||
|
data={recognizedTransactions}
|
||||||
|
sticky={true}
|
||||||
|
loading={false}
|
||||||
|
headerLoading={false}
|
||||||
|
expandColumnSpace={1}
|
||||||
|
expandToggleColumn={2}
|
||||||
|
selectionColumnWidth={45}
|
||||||
|
TableCellRenderer={TableFastCell}
|
||||||
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
|
TableRowsRenderer={TableVirtualizedListRows}
|
||||||
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
ContextMenu={ActionsMenu}
|
||||||
|
onCellClick={handleCellClick}
|
||||||
|
// #TableVirtualizedListRows props.
|
||||||
|
vListrowHeight={'small' == 'small' ? 32 : 40}
|
||||||
|
vListrowHeight={40}
|
||||||
|
vListOverscanRowCount={0}
|
||||||
|
initialColumnsWidths={initialColumnsWidths}
|
||||||
|
onColumnResizing={handleColumnResizing}
|
||||||
|
noResults={<T id={'cash_flow.account_transactions.no_results'} />}
|
||||||
|
className="table-constrant"
|
||||||
|
payload={{
|
||||||
|
onExclude: handleExcludeClick,
|
||||||
|
onCategorize: handleCategorizeClick,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecognizedTransactionsTable = compose(
|
||||||
|
withAlertsActions,
|
||||||
|
withDrawerActions,
|
||||||
|
withBankingActions,
|
||||||
|
)(RecognizedTransactionsTableRoot);
|
||||||
|
|
||||||
|
const DashboardConstrantTable = styled(DataTable)`
|
||||||
|
.table {
|
||||||
|
.thead {
|
||||||
|
.th {
|
||||||
|
background: #fff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody {
|
||||||
|
.tr:last-child .td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||||
|
.table .tbody {
|
||||||
|
.tbody-inner .tr.no-results {
|
||||||
|
.td {
|
||||||
|
padding: 2rem 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody-inner {
|
||||||
|
.tr .td {
|
||||||
|
border-bottom: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { flatten, map } from 'lodash';
|
||||||
|
import { IntersectionObserver } from '@/components';
|
||||||
|
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||||
|
import { useRecognizedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
||||||
|
|
||||||
|
interface RecognizedTransactionsContextValue {
|
||||||
|
isRecongizedTransactionsLoading: boolean;
|
||||||
|
isRecognizedTransactionsFetching: boolean;
|
||||||
|
recognizedTransactions: Array<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RecognizedTransactionsContext =
|
||||||
|
React.createContext<RecognizedTransactionsContextValue>(
|
||||||
|
{} as RecognizedTransactionsContextValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
function flattenInfinityPagesData(data) {
|
||||||
|
return flatten(map(data.pages, (page) => page.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecognizedTransactionsTableBootProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account uncategorized transctions provider.
|
||||||
|
*/
|
||||||
|
function RecognizedTransactionsTableBoot({
|
||||||
|
children,
|
||||||
|
}: RecognizedTransactionsTableBootProps) {
|
||||||
|
const { accountId } = useAccountTransactionsContext();
|
||||||
|
|
||||||
|
// Fetches the uncategorized transactions.
|
||||||
|
const {
|
||||||
|
data: recognizedTransactionsPage,
|
||||||
|
isFetching: isRecognizedTransactionsFetching,
|
||||||
|
isLoading: isRecongizedTransactionsLoading,
|
||||||
|
isSuccess: isRecognizedTransactionsSuccess,
|
||||||
|
isFetchingNextPage: isUncategorizedTransactionFetchNextPage,
|
||||||
|
fetchNextPage: fetchNextrecognizedTransactionsPage,
|
||||||
|
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||||
|
} = useRecognizedBankTransactionsInfinity({
|
||||||
|
page_size: 50,
|
||||||
|
});
|
||||||
|
// Memorized the cashflow account transactions.
|
||||||
|
const recognizedTransactions = React.useMemo(
|
||||||
|
() =>
|
||||||
|
isRecognizedTransactionsSuccess
|
||||||
|
? flattenInfinityPagesData(recognizedTransactionsPage)
|
||||||
|
: [],
|
||||||
|
[recognizedTransactionsPage, isRecognizedTransactionsSuccess],
|
||||||
|
);
|
||||||
|
// Handle the observer ineraction.
|
||||||
|
const handleObserverInteract = React.useCallback(() => {
|
||||||
|
if (
|
||||||
|
!isRecognizedTransactionsFetching &&
|
||||||
|
hasUncategorizedTransactionsNextPage
|
||||||
|
) {
|
||||||
|
fetchNextrecognizedTransactionsPage();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isRecognizedTransactionsFetching,
|
||||||
|
hasUncategorizedTransactionsNextPage,
|
||||||
|
fetchNextrecognizedTransactionsPage,
|
||||||
|
]);
|
||||||
|
// Provider payload.
|
||||||
|
const provider = {
|
||||||
|
recognizedTransactions,
|
||||||
|
isRecognizedTransactionsFetching,
|
||||||
|
isRecongizedTransactionsLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecognizedTransactionsContext.Provider value={provider}>
|
||||||
|
{children}
|
||||||
|
<IntersectionObserver
|
||||||
|
onIntersect={handleObserverInteract}
|
||||||
|
enabled={!isUncategorizedTransactionFetchNextPage}
|
||||||
|
/>
|
||||||
|
</RecognizedTransactionsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useRecognizedTransactionsBoot = () =>
|
||||||
|
React.useContext(RecognizedTransactionsContext);
|
||||||
|
|
||||||
|
export { RecognizedTransactionsTableBoot, useRecognizedTransactionsBoot };
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||||
|
import { safeCallback } from '@/utils';
|
||||||
|
|
||||||
|
export function ActionsMenu({
|
||||||
|
payload: { onCategorize, onExclude },
|
||||||
|
row: { original },
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Categorize'}
|
||||||
|
onClick={safeCallback(onCategorize, original)}
|
||||||
|
/>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem text={'Exclude'} onClick={safeCallback(onExclude, original)} />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Group, Icon } from '@/components';
|
||||||
|
import { getColumnWidth } from '@/utils';
|
||||||
|
import React from 'react';
|
||||||
|
import { useRecognizedTransactionsBoot } from './RecognizedTransactionsTableBoot';
|
||||||
|
|
||||||
|
const getReportColWidth = (data, accessor, headerText) => {
|
||||||
|
return getColumnWidth(
|
||||||
|
data,
|
||||||
|
accessor,
|
||||||
|
{ magicSpacing: 10, minWidth: 100 },
|
||||||
|
headerText,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const recognizeAccessor = (transaction) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{transaction.assigned_category_formatted}</span>
|
||||||
|
<Icon
|
||||||
|
icon={'arrowRight'}
|
||||||
|
color={'#8F99A8'}
|
||||||
|
iconSize={12}
|
||||||
|
style={{ marginLeft: 8, marginRight: 8 }}
|
||||||
|
/>
|
||||||
|
<span>{transaction.assigned_account_name}</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const descriptionAccessor = (transaction) => {
|
||||||
|
return <span style={{ color: '#5F6B7C' }}>{transaction.description}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve uncategorized transactions columns table.
|
||||||
|
*/
|
||||||
|
export function useUncategorizedTransactionsColumns() {
|
||||||
|
const { recognizedTransactions: data } = useRecognizedTransactionsBoot();
|
||||||
|
|
||||||
|
const withdrawalWidth = getReportColWidth(
|
||||||
|
data,
|
||||||
|
'formatted_withdrawal_amount',
|
||||||
|
'Withdrawal',
|
||||||
|
);
|
||||||
|
const depositWidth = getReportColWidth(
|
||||||
|
data,
|
||||||
|
'formatted_deposit_amount',
|
||||||
|
'Deposit',
|
||||||
|
);
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Date',
|
||||||
|
accessor: 'formatted_date',
|
||||||
|
width: 110,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Description',
|
||||||
|
accessor: descriptionAccessor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Payee',
|
||||||
|
accessor: 'payee',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Recognize',
|
||||||
|
accessor: recognizeAccessor,
|
||||||
|
textOverview: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Rule',
|
||||||
|
accessor: 'bank_rule_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Deposit',
|
||||||
|
accessor: 'formatted_deposit_amount',
|
||||||
|
align: 'right',
|
||||||
|
width: depositWidth,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Withdrawal',
|
||||||
|
accessor: 'formatted_withdrawal_amount',
|
||||||
|
align: 'right',
|
||||||
|
width: withdrawalWidth,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ export function MatchTransaction({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
<span>{label}</span>
|
<span className={styles.label}>{label}</span>
|
||||||
<Text style={{ fontSize: 12, color: '#5C7080' }}>Date: {date}</Text>
|
<Text style={{ fontSize: 12, color: '#5C7080' }}>Date: {date}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
import {
|
||||||
|
useInfiniteQuery,
|
||||||
|
useMutation,
|
||||||
|
useQuery,
|
||||||
|
useQueryClient,
|
||||||
|
} from 'react-query';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import { transformToCamelCase } from '@/utils';
|
import { transformToCamelCase } from '@/utils';
|
||||||
|
|
||||||
@@ -118,7 +123,7 @@ export function useUnexcludeUncategorizedTransaction(props) {
|
|||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
(uncategorizedTransactionId: number) =>
|
(uncategorizedTransactionId: number) =>
|
||||||
apiRequest.post(
|
apiRequest.put(
|
||||||
`/cashflow/transactions/${uncategorizedTransactionId}/unexclude`,
|
`/cashflow/transactions/${uncategorizedTransactionId}/unexclude`,
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
@@ -145,3 +150,70 @@ export function useMatchTransaction(props?: any) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useRecognizedBankTransactionsInfinity(
|
||||||
|
query,
|
||||||
|
infinityProps,
|
||||||
|
axios,
|
||||||
|
) {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useInfiniteQuery(
|
||||||
|
['RECOGNIZED_BANK_TRANSACTIONS_INFINITY', query],
|
||||||
|
async ({ pageParam = 1 }) => {
|
||||||
|
const response = await apiRequest.http({
|
||||||
|
...axios,
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/banking/recognized`,
|
||||||
|
params: { page: pageParam, ...query },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
|
||||||
|
getNextPageParam: (lastPage) => {
|
||||||
|
const { pagination } = lastPage;
|
||||||
|
|
||||||
|
return pagination.total > pagination.page_size * pagination.page
|
||||||
|
? lastPage.pagination.page + 1
|
||||||
|
: undefined;
|
||||||
|
},
|
||||||
|
...infinityProps,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useExcludedBankTransactionsInfinity(
|
||||||
|
query,
|
||||||
|
infinityProps,
|
||||||
|
axios,
|
||||||
|
) {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useInfiniteQuery(
|
||||||
|
['EXCLUDED_BANK_TRANSACTIONS_INFINITY', query],
|
||||||
|
async ({ pageParam = 1 }) => {
|
||||||
|
const response = await apiRequest.http({
|
||||||
|
...axios,
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/cashflow/excluded`,
|
||||||
|
params: { page: pageParam, ...query },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
|
||||||
|
getNextPageParam: (lastPage) => {
|
||||||
|
const { pagination } = lastPage;
|
||||||
|
|
||||||
|
return pagination.total > pagination.page_size * pagination.page
|
||||||
|
? lastPage.pagination.page + 1
|
||||||
|
: undefined;
|
||||||
|
},
|
||||||
|
...infinityProps,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -611,4 +611,10 @@ export default {
|
|||||||
],
|
],
|
||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
},
|
},
|
||||||
|
arrowRight: {
|
||||||
|
path: [
|
||||||
|
'M14.7,7.29l-5-5C9.52,2.1,9.27,1.99,8.99,1.99c-0.55,0-1,0.45-1,1c0,0.28,0.11,0.53,0.29,0.71l3.29,3.29H1.99c-0.55,0-1,0.45-1,1s0.45,1,1,1h9.59l-3.29,3.29c-0.18,0.18-0.29,0.43-0.29,0.71c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29l5-5c0.18-0.18,0.29-0.43,0.29-0.71S14.88,7.47,14.7,7.29z',
|
||||||
|
],
|
||||||
|
viewBox: '0 0 16 16',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user