mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
feat(server): sync Plaid transactions to uncategorized transactions
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user