diff --git a/packages/server/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.js b/packages/server/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.js
index 74aca99ce..29a596772 100644
--- a/packages/server/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.js
+++ b/packages/server/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.js
@@ -7,6 +7,7 @@ exports.up = function (knex) {
table.decimal('amount');
table.string('currency_code');
table.string('reference_no').index();
+ table.string('payee');
table
.integer('account_id')
.unsigned()
diff --git a/packages/server/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.js b/packages/server/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.js
new file mode 100644
index 000000000..151f5a28b
--- /dev/null
+++ b/packages/server/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.js
@@ -0,0 +1,7 @@
+exports.up = function (knex) {
+ return knex.schema.table('accounts', (table) => {
+ table.integer('uncategorized_transactions').defaultTo(0);
+ });
+};
+
+exports.down = function (knex) {};
diff --git a/packages/server/src/interfaces/CashFlow.ts b/packages/server/src/interfaces/CashFlow.ts
index 2f6bf1adf..aab3bb766 100644
--- a/packages/server/src/interfaces/CashFlow.ts
+++ b/packages/server/src/interfaces/CashFlow.ts
@@ -264,6 +264,7 @@ export interface CreateUncategorizedTransactionDTO {
accountId: number;
amount: number;
currencyCode: string;
+ payee?: string;
description?: string;
referenceNo?: string | null;
plaidTransactionId?: string | null;
diff --git a/packages/server/src/interfaces/Plaid.ts b/packages/server/src/interfaces/Plaid.ts
index 88a0ebb3c..a8ad469df 100644
--- a/packages/server/src/interfaces/Plaid.ts
+++ b/packages/server/src/interfaces/Plaid.ts
@@ -38,7 +38,7 @@ export interface PlaidTransaction {
iso_currency_code: string;
transaction_id: string;
transaction_type: string;
- payment_meta: { reference_number: string | null };
+ payment_meta: { reference_number: string | null; payee: string | null };
}
export interface PlaidFetchedTransactionsUpdates {
diff --git a/packages/server/src/models/Account.ts b/packages/server/src/models/Account.ts
index 9d4fb053e..c46f9e77b 100644
--- a/packages/server/src/models/Account.ts
+++ b/packages/server/src/models/Account.ts
@@ -196,6 +196,7 @@ export default class Account extends mixin(TenantModel, [
const Expense = require('models/Expense');
const ExpenseEntry = require('models/ExpenseCategory');
const ItemEntry = require('models/ItemEntry');
+ const UncategorizedTransaction = require('models/UncategorizedCashflowTransaction');
return {
/**
@@ -305,6 +306,21 @@ export default class Account extends mixin(TenantModel, [
to: 'items_entries.sellAccountId',
},
},
+
+ /**
+ * Associated uncategorized transactions.
+ */
+ uncategorizedTransactions: {
+ relation: Model.HasManyRelation,
+ modelClass: UncategorizedTransaction.default,
+ join: {
+ from: 'accounts.id',
+ to: 'uncategorized_cashflow_transactions.accountId',
+ },
+ filter: (query) => {
+ query.filter('categorized', false);
+ },
+ },
};
}
diff --git a/packages/server/src/models/UncategorizedCashflowTransaction.ts b/packages/server/src/models/UncategorizedCashflowTransaction.ts
index bb6798504..d8f3db543 100644
--- a/packages/server/src/models/UncategorizedCashflowTransaction.ts
+++ b/packages/server/src/models/UncategorizedCashflowTransaction.ts
@@ -1,6 +1,7 @@
/* eslint-disable global-require */
import TenantModel from 'models/TenantModel';
import { Model } from 'objection';
+import Account from './Account';
export default class UncategorizedCashflowTransaction extends TenantModel {
amount: number;
@@ -80,4 +81,29 @@ export default class UncategorizedCashflowTransaction extends TenantModel {
},
};
}
+
+ /**
+ *
+ * @param queryContext
+ */
+ public async $afterInsert(queryContext) {
+ await super.$afterInsert(queryContext);
+
+ // Increments the uncategorized transactions count of the associated account.
+ await Account.query(queryContext.transaction)
+ .findById(this.accountId)
+ .increment('uncategorized_transactions', 1);
+ }
+
+ /**
+ *
+ * @param queryContext
+ */
+ public async $afterDelete(queryContext) {
+ await super.$afterDelete(queryContext);
+
+ await Account.query()
+ .findById(this.accountId)
+ .decrement('uncategorized_transactions', 1);
+ }
}
diff --git a/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts b/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts
index 456624493..530ecec2d 100644
--- a/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts
+++ b/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts
@@ -90,7 +90,7 @@ export class PlaidSyncDb {
tenantId,
uncategoriedDTO
),
- { concurrency: CONCURRENCY_ASYNC }
+ { concurrency: 1 }
);
}
diff --git a/packages/server/src/services/Banking/Plaid/utils.ts b/packages/server/src/services/Banking/Plaid/utils.ts
index 03bf2b50c..de582e852 100644
--- a/packages/server/src/services/Banking/Plaid/utils.ts
+++ b/packages/server/src/services/Banking/Plaid/utils.ts
@@ -44,8 +44,9 @@ export const transformPlaidTrxsToCashflowCreate = R.curry(
): CreateUncategorizedTransactionDTO => {
return {
date: plaidTranasction.date,
- description: plaidTranasction.name,
amount: plaidTranasction.amount,
+ description: plaidTranasction.name,
+ payee: plaidTranasction.payment_meta?.payee,
currencyCode: plaidTranasction.iso_currency_code,
accountId: cashflowAccountId,
referenceNo: plaidTranasction.payment_meta?.reference_number,
diff --git a/packages/server/src/services/Cashflow/CreateUncategorizedTransaction.ts b/packages/server/src/services/Cashflow/CreateUncategorizedTransaction.ts
index 9f40323d9..ccb2aca25 100644
--- a/packages/server/src/services/Cashflow/CreateUncategorizedTransaction.ts
+++ b/packages/server/src/services/Cashflow/CreateUncategorizedTransaction.ts
@@ -1,6 +1,6 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
-import UnitOfWork from '../UnitOfWork';
+import UnitOfWork, { IsolationLevel } from '../UnitOfWork';
import { Knex } from 'knex';
import { CreateUncategorizedTransactionDTO } from '@/interfaces';
@@ -21,15 +21,20 @@ export class CreateUncategorizedTransaction {
tenantId: number,
createDTO: CreateUncategorizedTransactionDTO
) {
- const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
+ const { UncategorizedCashflowTransaction, Account } =
+ this.tenancy.models(tenantId);
- return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
- const transaction = await UncategorizedCashflowTransaction.query(
- trx
- ).insertAndFetch({
- ...createDTO,
- });
- return transaction;
- });
+ return this.uow.withTransaction(
+ tenantId,
+ async (trx: Knex.Transaction) => {
+ const transaction = await UncategorizedCashflowTransaction.query(
+ trx
+ ).insertAndFetch({
+ ...createDTO,
+ });
+
+ return transaction;
+ },
+ );
}
}
diff --git a/packages/server/src/services/Cashflow/UncategorizedTransactionTransformer.ts b/packages/server/src/services/Cashflow/UncategorizedTransactionTransformer.ts
index 0162c8e0a..bf0a6a1cf 100644
--- a/packages/server/src/services/Cashflow/UncategorizedTransactionTransformer.ts
+++ b/packages/server/src/services/Cashflow/UncategorizedTransactionTransformer.ts
@@ -29,9 +29,12 @@ export class UncategorizedTransactionTransformer extends Transformer {
* @returns {string}
*/
protected formattetDepositAmount(transaction) {
- return formatNumber(transaction.deposit, {
- currencyCode: transaction.currencyCode,
- });
+ if (transaction.isDepositTransaction) {
+ return formatNumber(transaction.deposit, {
+ currencyCode: transaction.currencyCode,
+ });
+ }
+ return '';
}
/**
@@ -40,8 +43,11 @@ export class UncategorizedTransactionTransformer extends Transformer {
* @returns {string}
*/
protected formattedWithdrawalAmount(transaction) {
- return formatNumber(transaction.withdrawal, {
- currencyCode: transaction.currencyCode,
- });
+ if (transaction.isWithdrawalTransaction) {
+ return formatNumber(transaction.withdrawal, {
+ currencyCode: transaction.currencyCode,
+ });
+ }
+ return '';
}
}
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDetailsBar.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDetailsBar.tsx
index 7f4d64921..90e10eae0 100644
--- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDetailsBar.tsx
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDetailsBar.tsx
@@ -84,6 +84,18 @@ function AccountBankBalanceItem() {
);
}
+function AccountNumberItem() {
+ const { currentAccount } = useAccountTransactionsContext();
+
+ if (!currentAccount.account_mask) return null;
+
+ return (
+
+ Account Number: xxx{currentAccount.account_mask}
+
+ );
+}
+
function AccountTransactionsDetailsBarSkeleton() {
return (
@@ -101,6 +113,7 @@ function AccountTransactionsDetailsContent() {
return (
+
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx
index aede21571..c2d40059d 100644
--- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx
@@ -8,12 +8,17 @@ const AccountContentTabs = styled(ContentTabs)`
`;
export function AccountTransactionsFilterTabs() {
- const { filterTab, setFilterTab } = useAccountTransactionsContext();
+ const { filterTab, setFilterTab, currentAccount } =
+ useAccountTransactionsContext();
const handleChange = (value) => {
setFilterTab(value);
};
+ const hasUncategorizedTransx = Boolean(
+ currentAccount.uncategorized_transactions,
+ );
+
return (
-
- 20 Uncategorized
- Transactions
- >
- }
- description={'For Bank Statement'}
- />
+ {hasUncategorizedTransx && (
+
+
+ {currentAccount.uncategorized_transactions}
+ {' '}
+ Uncategorized Transactions
+ >
+ }
+ description={'For Bank Statement'}
+ />
+ )}