feat(server): sync Plaid transactions to uncategorized transactions

This commit is contained in:
Ahmed Bouhuolia
2024-03-04 13:42:17 +02:00
parent 9db03350e0
commit f23e8d98f6
10 changed files with 113 additions and 24 deletions

View File

@@ -23,7 +23,7 @@ export default class NewCashflowTransactionController extends BaseController {
const router = Router();
router.get(
'/transactions/uncategorized',
'/transactions/:id/uncategorized',
this.asyncMiddleware(this.getUncategorizedCashflowTransactions),
this.catchServiceErrors
);
@@ -237,10 +237,12 @@ export default class NewCashflowTransactionController extends BaseController {
next: NextFunction
) => {
const { tenantId } = req;
const { id: accountId } = req.params;
try {
const data = await this.cashflowApplication.getUncategorizedTransactions(
tenantId
tenantId,
accountId
);
return res.status(200).send(data);

View File

@@ -16,6 +16,7 @@ exports.up = function (knex) {
table.string('categorize_ref_type');
table.integer('categorize_ref_id').unsigned();
table.boolean('categorized').defaultTo(false);
table.string('plaid_transaction_id');
table.timestamps();
}
);

View File

@@ -257,3 +257,14 @@ export interface IUncategorizedCashflowTransaction {
categorizeRefId: number;
categorized: boolean;
}
export interface CreateUncategorizedTransactionDTO {
date: Date | string;
accountId: number;
amount: number;
currencyCode: string;
description?: string;
referenceNo?: string | null;
plaidTransactionId?: string | null;
}

View File

@@ -38,6 +38,7 @@ export interface PlaidTransaction {
iso_currency_code: string;
transaction_id: string;
transaction_type: string;
payment_meta: { reference_number: string | null };
}
export interface PlaidFetchedTransactionsUpdates {

View File

@@ -11,6 +11,7 @@ import {
import NewCashflowTransactionService from '@/services/Cashflow/NewCashflowTransactionService';
import { DeleteCashflowTransaction } from '@/services/Cashflow/DeleteCashflowTransactionService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
const CONCURRENCY_ASYNC = 10;
@@ -22,6 +23,9 @@ export class PlaidSyncDb {
@Inject()
private createAccountService: CreateAccount;
@Inject()
private cashflowApp: CashflowApplication;
@Inject()
private createCashflowTransactionService: NewCashflowTransactionService;
@@ -75,15 +79,16 @@ export class PlaidSyncDb {
cashflowAccount.id,
openingEquityBalance.id
);
const accountsCashflowDTO = R.map(transformTransaction)(plaidTranasctions);
const uncategorizedTransDTOs =
R.map(transformTransaction)(plaidTranasctions);
// Creating account transaction queue.
await bluebird.map(
accountsCashflowDTO,
(cashflowDTO) =>
this.createCashflowTransactionService.newCashflowTransaction(
uncategorizedTransDTOs,
(uncategoriedDTO) =>
this.cashflowApp.createUncategorizedTransaction(
tenantId,
cashflowDTO
uncategoriedDTO
),
{ concurrency: CONCURRENCY_ASYNC }
);

View File

@@ -1,7 +1,9 @@
import * as R from 'ramda';
import {
CreateUncategorizedTransactionDTO,
IAccountCreateDTO,
ICashflowNewCommandDTO,
IUncategorizedCashflowTransaction,
PlaidAccount,
PlaidTransaction,
} from '@/interfaces';
@@ -32,30 +34,22 @@ export const transformPlaidAccountToCreateAccount = (
* @param {number} cashflowAccountId - Cashflow account ID.
* @param {number} creditAccountId - Credit account ID.
* @param {PlaidTransaction} plaidTranasction - Plaid transaction.
* @returns {ICashflowNewCommandDTO}
* @returns {CreateUncategorizedTransactionDTO}
*/
export const transformPlaidTrxsToCashflowCreate = R.curry(
(
cashflowAccountId: number,
creditAccountId: number,
plaidTranasction: PlaidTransaction
): ICashflowNewCommandDTO => {
): CreateUncategorizedTransactionDTO => {
return {
date: plaidTranasction.date,
transactionType: 'OwnerContribution',
description: plaidTranasction.name,
amount: plaidTranasction.amount,
exchangeRate: 1,
currencyCode: plaidTranasction.iso_currency_code,
creditAccountId,
cashflowAccountId,
// transactionNumber: string;
// referenceNo: string;
accountId: cashflowAccountId,
referenceNo: plaidTranasction.payment_meta?.reference_number,
plaidTransactionId: plaidTranasction.transaction_id,
publish: true,
};
}
);

View File

@@ -4,10 +4,13 @@ import { UncategorizeCashflowTransaction } from './UncategorizeCashflowTransacti
import { CategorizeCashflowTransaction } from './CategorizeCashflowTransaction';
import {
CategorizeTransactionAsExpenseDTO,
CreateUncategorizedTransactionDTO,
ICategorizeCashflowTransactioDTO,
IUncategorizedCashflowTransaction,
} from '@/interfaces';
import { CategorizeTransactionAsExpense } from './CategorizeTransactionAsExpense';
import { GetUncategorizedTransactions } from './GetUncategorizedTransactions';
import { CreateUncategorizedTransaction } from './CreateUncategorizedTransaction';
@Service()
export class CashflowApplication {
@@ -26,6 +29,9 @@ export class CashflowApplication {
@Inject()
private getUncategorizedTransactionsService: GetUncategorizedTransactions;
@Inject()
private createUncategorizedTransactionService: CreateUncategorizedTransaction;
/**
* Deletes the given cashflow transaction.
* @param {number} tenantId
@@ -39,6 +45,22 @@ export class CashflowApplication {
);
}
/**
* Creates a new uncategorized cash transaction.
* @param {number} tenantId
* @param {CreateUncategorizedTransactionDTO} createUncategorizedTransactionDTO
* @returns {IUncategorizedCashflowTransaction}
*/
public createUncategorizedTransaction(
tenantId: number,
createUncategorizedTransactionDTO: CreateUncategorizedTransactionDTO
) {
return this.createUncategorizedTransactionService.create(
tenantId,
createUncategorizedTransactionDTO
);
}
/**
* Uncategorize the given cashflow transaction.
* @param {number} tenantId
@@ -98,7 +120,10 @@ export class CashflowApplication {
* @param {number} tenantId
* @returns {}
*/
public getUncategorizedTransactions(tenantId: number) {
return this.getUncategorizedTransactionsService.getTransactions(tenantId);
public getUncategorizedTransactions(tenantId: number, accountId: number) {
return this.getUncategorizedTransactionsService.getTransactions(
tenantId,
accountId
);
}
}

View File

@@ -0,0 +1,35 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { Knex } from 'knex';
import { CreateUncategorizedTransactionDTO } from '@/interfaces';
@Service()
export class CreateUncategorizedTransaction {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
/**
* Creates an uncategorized cashflow transaction.
* @param {number} tenantId
* @param {CreateUncategorizedTransactionDTO} createDTO
*/
public create(
tenantId: number,
createDTO: CreateUncategorizedTransactionDTO
) {
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
const transaction = await UncategorizedCashflowTransaction.query(
trx
).insertAndFetch({
...createDTO,
});
return transaction;
});
}
}

View File

@@ -13,13 +13,15 @@ export class GetUncategorizedTransactions {
/**
* Retrieves the uncategorized cashflow transactions.
* @param {number} tenantId
* @param {number} tenantId - Tenant id.
* @param {number} accountId - Account Id.
*/
public async getTransactions(tenantId: number) {
public async getTransactions(tenantId: number, accountId: number) {
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
const { results, pagination } =
await UncategorizedCashflowTransaction.query()
.where('accountId', accountId)
.where('categorized', false)
.withGraphFetched('account')
.pagination(0, 10);

View File

@@ -7,9 +7,22 @@ export class UncategorizedTransactionTransformer extends Transformer {
* @returns {string[]}
*/
public includeAttributes = (): string[] => {
return ['formattetDepositAmount', 'formattedWithdrawalAmount'];
return [
'formattedDate',
'formattetDepositAmount',
'formattedWithdrawalAmount',
];
};
/**
* Formattes the transaction date.
* @param transaction
* @returns {string}
*/
public formattedDate(transaction) {
return this.formatDate(transaction.date);
}
/**
* Formatted deposit amount.
* @param transaction