diff --git a/packages/server/src/interfaces/CashflowService.ts b/packages/server/src/interfaces/CashflowService.ts index c1f55a86f..415682ae5 100644 --- a/packages/server/src/interfaces/CashflowService.ts +++ b/packages/server/src/interfaces/CashflowService.ts @@ -167,11 +167,18 @@ export interface CategorizeTransactionAsExpenseDTO { export interface IGetUncategorizedTransactionsQuery { page?: number; pageSize?: number; + minDate?: Date; + maxDate?: Date; + minAmount?: number; + maxAmount?: number; } - export interface IGetRecognizedTransactionsQuery { page?: number; pageSize?: number; accountId?: number; -} \ No newline at end of file + minDate?: Date; + maxDate?: Date; + minAmount?: number; + maxAmount?: number; +} diff --git a/packages/server/src/models/UncategorizedCashflowTransaction.ts b/packages/server/src/models/UncategorizedCashflowTransaction.ts index 218c35982..1040fe0ca 100644 --- a/packages/server/src/models/UncategorizedCashflowTransaction.ts +++ b/packages/server/src/models/UncategorizedCashflowTransaction.ts @@ -1,5 +1,5 @@ /* eslint-disable global-require */ -import * as R from 'ramda'; +import moment from 'moment'; import { Model, ModelOptions, QueryContext, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import ModelSettings from './ModelSetting'; @@ -46,7 +46,7 @@ export default class UncategorizedCashflowTransaction extends mixin( 'isDepositTransaction', 'isWithdrawalTransaction', 'isRecognized', - 'isExcluded' + 'isExcluded', ]; } @@ -143,6 +143,42 @@ export default class UncategorizedCashflowTransaction extends mixin( query.whereNull('categorizeRefType'); query.whereNull('categorizeRefId'); }, + + /** + * Filters the not pending transactions. + */ + notPending(query) { + query.where('pending', false); + }, + + /** + * Filters the pending transactions. + */ + pending(query) { + query.where('pending', true); + }, + + minAmount(query, minAmount) { + query.where('amount', '>=', minAmount); + }, + + maxAmount(query, maxAmount) { + query.where('amount', '<=', maxAmount); + }, + + toDate(query, toDate) { + const dateFormat = 'YYYY-MM-DD'; + const _toDate = moment(toDate).endOf('day').format(dateFormat); + + query.where('date', '<=', _toDate); + }, + + fromDate(query, fromDate) { + const dateFormat = 'YYYY-MM-DD'; + const _fromDate = moment(fromDate).startOf('day').format(dateFormat); + + query.where('date', '>=', _fromDate); + }, }; } diff --git a/packages/server/src/services/Banking/Exclude/GetExcludedBankTransactions.ts b/packages/server/src/services/Banking/Exclude/GetExcludedBankTransactions.ts index 41435f1b8..43bc523de 100644 --- a/packages/server/src/services/Banking/Exclude/GetExcludedBankTransactions.ts +++ b/packages/server/src/services/Banking/Exclude/GetExcludedBankTransactions.ts @@ -1,5 +1,6 @@ -import HasTenancyService from '@/services/Tenancy/TenancyService'; import { Inject, Service } from 'typedi'; +import moment from 'moment'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; import { ExcludedBankTransactionsQuery } from './_types'; import { UncategorizedTransactionTransformer } from '@/services/Cashflow/UncategorizedTransactionTransformer'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; @@ -39,6 +40,18 @@ export class GetExcludedBankTransactionsService { if (_query.accountId) { q.where('account_id', _query.accountId); } + if (_query.minDate) { + q.modify('fromDate', _query.minDate); + } + if (_query.maxDate) { + q.modify('toDate', _query.maxDate); + } + if (_query.minAmount) { + q.modify('minAmount', _query.minAmount); + } + if (_query.maxAmount) { + q.modify('maxAmount', _query.maxAmount); + } }) .pagination(_query.page - 1, _query.pageSize); diff --git a/packages/server/src/services/Banking/Exclude/_types.ts b/packages/server/src/services/Banking/Exclude/_types.ts index c7aa571fd..5d1e2842d 100644 --- a/packages/server/src/services/Banking/Exclude/_types.ts +++ b/packages/server/src/services/Banking/Exclude/_types.ts @@ -4,6 +4,10 @@ export interface ExcludedBankTransactionsQuery { page?: number; pageSize?: number; accountId?: number; + minDate?: Date; + maxDate?: Date; + minAmount?: number; + maxAmount?: number; } export interface IBankTransactionUnexcludingEventPayload { diff --git a/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts b/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts index 9ba2bd102..1fec81968 100644 --- a/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts +++ b/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts @@ -23,7 +23,7 @@ export class GetRecognizedTransactionsService { ) { const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId); - const _filter = { + const _query = { page: 1, pageSize: 20, ...filter, @@ -36,11 +36,23 @@ export class GetRecognizedTransactionsService { q.whereNotNull('recognizedTransactionId'); q.modify('notExcluded'); - if (_filter.accountId) { - q.where('accountId', _filter.accountId); + if (_query.accountId) { + q.where('accountId', _query.accountId); + } + if (_query.minDate) { + q.modify('fromDate', _query.minDate); + } + if (_query.maxDate) { + q.modify('toDate', _query.maxDate); + } + if (_query.minAmount) { + q.modify('minAmount', _query.minAmount); + } + if (_query.maxAmount) { + q.modify('maxAmount', _query.maxAmount); } }) - .pagination(_filter.page - 1, _filter.pageSize); + .pagination(_query.page - 1, _query.pageSize); const data = await this.transformer.transform( tenantId, diff --git a/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts b/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts index 7b3c76774..7d8da347b 100644 --- a/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts +++ b/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts @@ -60,6 +60,19 @@ export class GetUncategorizedTransactions { q.whereNull('matchedBankTransactions.id'); q.orderBy('date', 'DESC'); + + if (_query.minDate) { + q.modify('fromDate', _query.minDate); + } + if (_query.maxDate) { + q.modify('toDate', _query.maxDate); + } + if (_query.minAmount) { + q.modify('minAmount', _query.minAmount); + } + if (_query.maxAmount) { + q.modify('maxAmount', _query.maxAmount); + } }) .pagination(_query.page - 1, _query.pageSize); diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx index 2272c7e54..261a638f0 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx @@ -1,8 +1,21 @@ // @ts-nocheck +import * as R from 'ramda'; +import * as Yup from 'yup'; +import { useMemo } from 'react'; import { useAppQueryString } from '@/hooks'; -import { Group } from '@/components'; +import { Box, FDateInput, FFormGroup, Group, Icon, Stack } from '@/components'; import { useAccountTransactionsContext } from './AccountTransactionsProvider'; import { TagsControl } from '@/components/TagsControl'; +import { + Button, + Classes, + FormGroup, + Intent, + Popover, + Position, +} from '@blueprintjs/core'; +import { DateInput } from '@blueprintjs/datetime'; +import { Form, Formik } from 'formik'; export function AccountTransactionsUncategorizeFilter() { const { bankAccountMetaSummary } = useAccountTransactionsContext(); @@ -48,3 +61,70 @@ export function AccountTransactionsUncategorizeFilter() { ); } + +const initialValues = { + fromDate: '', + toDate: '', +}; + +const validationSchema = Yup.object().shape({ + fromDate: Yup.date() + .nullable() + .required('From Date is required') + .max(Yup.ref('toDate'), 'From Date cannot be after To Date'), + toDate: Yup.date() + .nullable() + .required('To Date is required') + .min(Yup.ref('fromDate'), 'To Date cannot be before From Date'), +}); + +function UncategorizedTransactionsDateFilter() { + const handleSubmit = () => {}; + + return ( + +
+ + + + date.toLocaleDateString()} + parseDate={(str) => new Date(str)} + inputProps={{ + fill: true, + leftElement: , + }} + style={{ marginBottom: 0 }} + /> + + + + date.toLocaleDateString()} + parseDate={(str) => new Date(str)} + inputProps={{ + fill: true, + leftElement: , + }} + style={{ marginBottom: 0 }} + /> + + + + + + + + +
+
+ ); +}