diff --git a/server/src/api/controllers/Accounts.ts b/server/src/api/controllers/Accounts.ts index ccce9c4a8..00df6347f 100644 --- a/server/src/api/controllers/Accounts.ts +++ b/server/src/api/controllers/Accounts.ts @@ -23,6 +23,14 @@ export default class AccountsController extends BaseController { router() { const router = Router(); + router.get( + '/transactions', + [ + query('account_id').optional().isInt().toInt(), + ], + this.asyncMiddleware(this.accountTransactions.bind(this)), + this.catchServiceErrors, + ); router.post( '/:id/activate', [...this.accountParamSchema], @@ -329,6 +337,28 @@ export default class AccountsController extends BaseController { } } + /** + * Retrieve accounts transactions list. + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {Response} + */ + async accountTransactions(req: Request, res: Response, next: NextFunction) { + const { tenantId } = req; + const transactionsFilter = this.matchedQueryData(req); + + try { + const { transactions } = await this.accountsService.getAccountsTransactions( + tenantId, + transactionsFilter + ); + return res.status(200).send({ transactions }); + } catch (error) { + next(error); + } + } + /** * Transforms service errors to response. * @param {Error} diff --git a/server/src/interfaces/Account.ts b/server/src/interfaces/Account.ts index 1893a3ade..39303e829 100644 --- a/server/src/interfaces/Account.ts +++ b/server/src/interfaces/Account.ts @@ -28,6 +28,13 @@ export interface IAccount { accountParentType: string, }; +export interface IAccountsTransactionsFilter { + accountId?: number, +} + +export interface IAccountTransaction { + +} export interface IAccountResponse extends IAccount { } diff --git a/server/src/models/AccountTransaction.js b/server/src/models/AccountTransaction.js index fc886beaf..9465084a4 100644 --- a/server/src/models/AccountTransaction.js +++ b/server/src/models/AccountTransaction.js @@ -128,6 +128,7 @@ export default class AccountTransaction extends TenantModel { */ static get relationMappings() { const Account = require('models/Account'); + const Contact = require('models/Contact'); return { account: { @@ -138,6 +139,14 @@ export default class AccountTransaction extends TenantModel { to: 'accounts.id', }, }, + contact: { + relation: Model.BelongsToOneRelation, + modelClass: Contact.default, + join: { + from: 'accounts_transactions.contactId', + to: 'contacts.id', + }, + }, }; } } diff --git a/server/src/models/ManualJournalEntry.js b/server/src/models/ManualJournalEntry.js index 70c08ed49..c003e1f38 100644 --- a/server/src/models/ManualJournalEntry.js +++ b/server/src/models/ManualJournalEntry.js @@ -21,6 +21,7 @@ export default class ManualJournalEntry extends TenantModel { */ static get relationMappings() { const Account = require('models/Account'); + const Contact = require('models/Contact'); return { account: { @@ -31,6 +32,14 @@ export default class ManualJournalEntry extends TenantModel { to: 'accounts.id', }, }, + contact: { + relation: Model.BelongsToOneRelation, + modelClass: Contact.default, + join: { + from: 'manual_journals_entries.contactId', + to: 'contacts.id', + }, + }, }; } } diff --git a/server/src/services/Accounts/AccountsService.ts b/server/src/services/Accounts/AccountsService.ts index 98aa8502c..30945705b 100644 --- a/server/src/services/Accounts/AccountsService.ts +++ b/server/src/services/Accounts/AccountsService.ts @@ -8,7 +8,9 @@ import { IAccount, IAccountsFilter, IFilterMeta, - IAccountResponse + IAccountResponse, + IAccountsTransactionsFilter, + IAccountTransaction } from 'interfaces'; import { EventDispatcher, @@ -317,7 +319,8 @@ export default class AccountsService { * @param {number} accountId */ public async getAccount(tenantId: number, accountId: number) { - return this.getAccountOrThrowError(tenantId, accountId); + const account = await this.getAccountOrThrowError(tenantId, accountId); + return this.transformAccountResponse(tenantId, account); } /** @@ -628,6 +631,45 @@ export default class AccountsService { }; } + /** + * Retrieve the accounts transactions. + * @param {number} tenantId - + * @param {IAccountsTransactionsFilter} filter - + */ + public async getAccountsTransactions( + tenantId: number, + filter: IAccountsTransactionsFilter, + ): Promise<{ transactions: IAccountTransaction }> { + const { AccountTransaction } = this.tenancy.models(tenantId); + + this.logger.info('[accounts] trying to get accounts transactions list.'); + const transactions = await AccountTransaction.query().onBuild((query) => { + query.orderBy('date', 'DESC'); + + if (filter.accountId) { + query.where('account_id', filter.accountId); + } + query.withGraphFetched('account'); + query.withGraphFetched('contact'); + }); + return { transactions }; + } + + /** + * Transformes the account model to specific account response. + */ + private transformAccountResponse(tenantId: number, account: IAccount) { + const settings = this.tenancy.settings(tenantId); + const baseCurrency = settings.get({ + group: 'organization', + key: 'base_currency', + }); + return { + ...account, + currencyCode: baseCurrency, + }; + } + /** * Transformes the accounts models to accounts response. */ diff --git a/server/src/services/Expenses/ExpensesService.ts b/server/src/services/Expenses/ExpensesService.ts index 13c239952..1e3670e08 100644 --- a/server/src/services/Expenses/ExpensesService.ts +++ b/server/src/services/Expenses/ExpensesService.ts @@ -641,7 +641,7 @@ export default class ExpensesService implements IExpensesService { const expense = await expenseRepository.findOneById(expenseId, [ 'paymentAccount', - 'categories', + 'categories.expenseAccount', ]); if (!expense) { throw new ServiceError(ERRORS.EXPENSE_NOT_FOUND); diff --git a/server/src/services/ManualJournals/ManualJournalsService.ts b/server/src/services/ManualJournals/ManualJournalsService.ts index bedd8ab63..1261fec4a 100644 --- a/server/src/services/ManualJournals/ManualJournalsService.ts +++ b/server/src/services/ManualJournals/ManualJournalsService.ts @@ -822,7 +822,8 @@ export default class ManualJournalsService implements IManualJournalsService { ); const manualJournal = await ManualJournal.query() .findById(manualJournalId) - .withGraphFetched('entries') + .withGraphFetched('entries.account') + .withGraphFetched('entries.contact') .withGraphFetched('transactions') .withGraphFetched('media');