refactor(nestjs): hook up new endpoints

This commit is contained in:
Ahmed Bouhuolia
2025-05-16 01:41:11 +02:00
parent ecb80b2cf2
commit 4de1ef71ca
23 changed files with 644 additions and 38 deletions

View File

@@ -1,12 +1,25 @@
import { Controller, Param, Post } from '@nestjs/common';
import { Controller, Get, Param, Post, Query } from '@nestjs/common';
import { BankAccountsApplication } from './BankAccountsApplication.service';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ICashflowAccountsFilter } from './types/BankAccounts.types';
@Controller('banking/accounts')
@ApiTags('banking-accounts')
export class BankAccountsController {
constructor(private bankAccountsApplication: BankAccountsApplication) {}
@Get()
@ApiOperation({ summary: 'Retrieve the bank accounts.' })
getBankAccounts(@Query() filterDto: ICashflowAccountsFilter) {
return this.bankAccountsApplication.getBankAccounts(filterDto);
}
@Get(':bankAccountId/summary')
@ApiOperation({ summary: 'Retrieve the bank account summary.' })
getBankAccountSummary(@Param('bankAccountId') bankAccountId: number) {
return this.bankAccountsApplication.getBankAccountSumnmary(bankAccountId);
}
@Post(':id/disconnect')
@ApiOperation({
summary: 'Disconnect the bank connection of the given bank account.',

View File

@@ -12,6 +12,9 @@ import { PlaidModule } from '../Plaid/Plaid.module';
import { BankRulesModule } from '../BankRules/BankRules.module';
import { BankingTransactionsRegonizeModule } from '../BankingTranasctionsRegonize/BankingTransactionsRegonize.module';
import { BankingTransactionsModule } from '../BankingTransactions/BankingTransactions.module';
import { GetBankAccountsService } from './queries/GetBankAccounts';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { GetBankAccountSummary } from './queries/GetBankAccountSummary';
@Module({
imports: [
@@ -20,6 +23,7 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac
BankRulesModule,
BankingTransactionsRegonizeModule,
BankingTransactionsModule,
DynamicListModule
],
providers: [
DisconnectBankAccountService,
@@ -29,6 +33,8 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac
DeleteUncategorizedTransactionsOnAccountDeleting,
DisconnectPlaidItemOnAccountDeleted,
BankAccountsApplication,
GetBankAccountsService,
GetBankAccountSummary
],
exports: [BankAccountsApplication],
controllers: [BankAccountsController],

View File

@@ -3,16 +3,39 @@ import { DisconnectBankAccountService } from './commands/DisconnectBankAccount.s
import { RefreshBankAccountService } from './commands/RefreshBankAccount.service';
import { ResumeBankAccountFeedsService } from './commands/ResumeBankAccountFeeds.service';
import { PauseBankAccountFeeds } from './commands/PauseBankAccountFeeds.service';
import { GetBankAccountsService } from './queries/GetBankAccounts';
import { ICashflowAccountsFilter } from './types/BankAccounts.types';
import { GetBankAccountSummary } from './queries/GetBankAccountSummary';
@Injectable()
export class BankAccountsApplication {
constructor(
private disconnectBankAccountService: DisconnectBankAccountService,
private readonly getBankAccountsService: GetBankAccountsService,
private readonly getBankAccountSummaryService: GetBankAccountSummary,
private readonly disconnectBankAccountService: DisconnectBankAccountService,
private readonly refreshBankAccountService: RefreshBankAccountService,
private readonly resumeBankAccountFeedsService: ResumeBankAccountFeedsService,
private readonly pauseBankAccountFeedsService: PauseBankAccountFeeds,
) {}
/**
* Retrieves the bank accounts.
* @param {ICashflowAccountsFilter} filterDto -
*/
getBankAccounts(filterDto: ICashflowAccountsFilter) {
return this.getBankAccountsService.getCashflowAccounts(filterDto);
}
/**
* Retrieves the given bank account summary.
* @param {number} bankAccountId
*/
getBankAccountSumnmary(bankAccountId: number) {
return this.getBankAccountSummaryService.getBankAccountSummary(
bankAccountId,
);
}
/**
* Disconnects the given bank account.
* @param {number} bankAccountId - Bank account identifier.

View File

@@ -0,0 +1,61 @@
import { Inject, Injectable } from '@nestjs/common';
import { ACCOUNT_TYPE } from '@/constants/accounts';
import { Account } from '@/modules/Accounts/models/Account.model';
import { CashflowAccountTransformer } from '@/modules/BankingTransactions/queries/BankAccountTransformer';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { ICashflowAccountsFilter } from '../types/BankAccounts.types';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
@Injectable()
export class GetBankAccountsService {
constructor(
private readonly dynamicListService: DynamicListService,
private readonly transformer: TransformerInjectable,
@Inject(Account.name)
private readonly accountModel: TenantModelProxy<typeof Account>,
) {}
/**
* Retrieve the cash flow accounts.
* @param {ICashflowAccountsFilter} filterDTO - Filter DTO.
* @returns {ICashflowAccount[]}
*/
public async getCashflowAccounts(filterDTO: ICashflowAccountsFilter) {
const _filterDto = {
sortOrder: 'desc',
columnSortBy: 'created_at',
inactiveMode: false,
...filterDTO,
};
// Parsees accounts list filter DTO.
const filter = this.dynamicListService.parseStringifiedFilter(_filterDto);
// Dynamic list service.
const dynamicList = await this.dynamicListService.dynamicList(
this.accountModel(),
filter,
);
// Retrieve accounts model based on the given query.
const accounts = await this.accountModel()
.query()
.onBuild((builder) => {
dynamicList.buildQuery()(builder);
builder.whereIn('account_type', [
ACCOUNT_TYPE.BANK,
ACCOUNT_TYPE.CASH,
ACCOUNT_TYPE.CREDIT_CARD,
]);
builder.modify('inactiveMode', filter.inactiveMode);
});
// Retrieves the transformed accounts.
const transformed = await this.transformer.transform(
accounts,
new CashflowAccountTransformer(),
);
return transformed;
}
}

View File

@@ -1,3 +1,4 @@
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { Knex } from 'knex';
export interface IBankAccountDisconnectingEventPayload {
@@ -15,3 +16,11 @@ export const ERRORS = {
BANK_ACCOUNT_FEEDS_ALREADY_PAUSED: 'BANK_ACCOUNT_FEEDS_ALREADY_PAUSED',
BANK_ACCOUNT_FEEDS_ALREADY_RESUMED: 'BANK_ACCOUNT_FEEDS_ALREADY_RESUMED',
};
export interface ICashflowAccountsFilter extends IDynamicListFilter{
page: number;
pageSize: number;
inactiveMode: boolean;
viewSlug?: string;
}

View File

@@ -9,7 +9,10 @@ import {
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
import { IBankAccountsFilter } from './types/BankingTransactions.types';
import {
IBankAccountsFilter,
ICashflowAccountTransactionsQuery,
} from './types/BankingTransactions.types';
import { CreateBankTransactionDto } from './dtos/CreateBankTransaction.dto';
@Controller('banking/transactions')
@@ -19,9 +22,13 @@ export class BankingTransactionsController {
private readonly bankingTransactionsApplication: BankingTransactionsApplication,
) {}
@Get('')
async getBankAccounts(@Query() filterDTO: IBankAccountsFilter) {
return this.bankingTransactionsApplication.getBankAccounts(filterDTO);
@Get()
async getBankAccountTransactions(
@Query() query: ICashflowAccountTransactionsQuery,
) {
return this.bankingTransactionsApplication.getBankAccountTransactions(
query,
);
}
@Post()

View File

@@ -24,6 +24,8 @@ import { GetBankAccountsService } from './queries/GetBankAccounts.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { BankAccount } from './models/BankAccount';
import { LedgerModule } from '../Ledger/Ledger.module';
import { GetBankAccountTransactionsService } from './queries/GetBankAccountTransactions/GetBankAccountTransactions.service';
import { GetBankAccountTransactionsRepository } from './queries/GetBankAccountTransactions/GetBankAccountTransactionsRepo.service';
const models = [
RegisterTenancyModel(UncategorizedBankTransaction),
@@ -57,6 +59,8 @@ const models = [
CommandBankTransactionValidator,
BranchTransactionDTOTransformer,
RemovePendingUncategorizedTransaction,
GetBankAccountTransactionsRepository,
GetBankAccountTransactionsService,
],
exports: [...models, RemovePendingUncategorizedTransaction],
})

View File

@@ -4,9 +4,11 @@ import { CreateBankTransactionService } from './commands/CreateBankTransaction.s
import { GetBankTransactionService } from './queries/GetBankTransaction.service';
import {
IBankAccountsFilter,
ICashflowAccountTransactionsQuery,
} from './types/BankingTransactions.types';
import { GetBankAccountsService } from './queries/GetBankAccounts.service';
import { CreateBankTransactionDto } from './dtos/CreateBankTransaction.dto';
import { GetBankAccountTransactionsService } from './queries/GetBankAccountTransactions/GetBankAccountTransactions.service';
@Injectable()
export class BankingTransactionsApplication {
@@ -15,6 +17,7 @@ export class BankingTransactionsApplication {
private readonly deleteTransactionService: DeleteCashflowTransaction,
private readonly getCashflowTransactionService: GetBankTransactionService,
private readonly getBankAccountsService: GetBankAccountsService,
private readonly getBankAccountTransactionsService: GetBankAccountTransactionsService,
) {}
/**
@@ -37,6 +40,16 @@ export class BankingTransactionsApplication {
);
}
/**
* Retrieves the bank transactions of the given bank id.
* @param {ICashflowAccountTransactionsQuery} query
*/
public getBankAccountTransactions(query: ICashflowAccountTransactionsQuery) {
return this.getBankAccountTransactionsService.bankAccountTransactions(
query,
);
}
/**
* Retrieves specific cashflow transaction.
* @param {number} cashflowTransactionId

View File

@@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { getBankAccountTransactionsDefaultQuery } from './_utils';
import { GetBankAccountTransactionsRepository } from './GetBankAccountTransactionsRepo.service';
import { GetBankAccountTransactions } from './GetBankAccountTransactions';
import { ICashflowAccountTransactionsQuery } from '../../types/BankingTransactions.types';
@Injectable()
export class GetBankAccountTransactionsService {
constructor(
private readonly getBankAccountTransactionsRepository: GetBankAccountTransactionsRepository,
) {}
/**
* Retrieve the cashflow account transactions report data.
* @param {ICashflowAccountTransactionsQuery} query -
* @return {Promise<IInvetoryItemDetailDOO>}
*/
public async bankAccountTransactions(
query: ICashflowAccountTransactionsQuery,
) {
const parsedQuery = {
...getBankAccountTransactionsDefaultQuery(),
...query,
};
this.getBankAccountTransactionsRepository.setQuery(parsedQuery);
await this.getBankAccountTransactionsRepository.asyncInit();
// Retrieve the computed report.
const report = new GetBankAccountTransactions(
this.getBankAccountTransactionsRepository,
parsedQuery,
);
const transactions = report.reportData();
const pagination = this.getBankAccountTransactionsRepository.pagination;
return { transactions, pagination };
}
}

View File

@@ -0,0 +1,201 @@
// @ts-nocheck
import R from 'ramda';
import moment from 'moment';
import { first, isEmpty } from 'lodash';
import {
ICashflowAccountTransaction,
ICashflowAccountTransactionsQuery,
} from '../../types/BankingTransactions.types';
import { BankTransactionStatus } from './_constants';
import { FinancialSheet } from '@/modules/FinancialStatements/common/FinancialSheet';
import { formatBankTransactionsStatus } from './_utils';
import { GetBankAccountTransactionsRepository } from './GetBankAccountTransactionsRepo.service';
import { runningBalance } from '@/utils/running-balance';
export class GetBankAccountTransactions extends FinancialSheet {
private runningBalance: any;
private query: ICashflowAccountTransactionsQuery;
private repo: GetBankAccountTransactionsRepository;
/**
* Constructor method.
* @param {IAccountTransaction[]} transactions -
* @param {number} openingBalance -
* @param {ICashflowAccountTransactionsQuery} query -
*/
constructor(
repo: GetBankAccountTransactionsRepository,
query: ICashflowAccountTransactionsQuery,
) {
super();
this.repo = repo;
this.query = query;
this.runningBalance = runningBalance(this.repo.openingBalance);
}
/**
* Retrieves the transaction status.
* @param {} transaction
* @returns {BankTransactionStatus}
*/
private getTransactionStatus(transaction: any): BankTransactionStatus {
const categorizedTrans = this.repo.uncategorizedTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
const matchedTrans = this.repo.matchedBankTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
if (!isEmpty(categorizedTrans)) {
return BankTransactionStatus.Categorized;
} else if (!isEmpty(matchedTrans)) {
return BankTransactionStatus.Matched;
} else {
return BankTransactionStatus.Manual;
}
}
/**
* Retrieves the uncategoized transaction id from the given transaction.
* @param transaction
* @returns {number|null}
*/
private getUncategorizedTransId(transaction: any): number {
// The given transaction would be categorized, matched or not, so we'd take a look at
// the categorized transaction first to get the id if not exist, then should look at the matched
// transaction if not exist too, so the given transaction has no uncategorized transaction id.
const categorizedTrans = this.repo.uncategorizedTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
const matchedTrans = this.repo.matchedBankTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
// Relation between the transaction and matching always been one-to-one.
const firstCategorizedTrans = first(categorizedTrans);
const firstMatchedTrans = first(matchedTrans);
return (
firstCategorizedTrans?.id ||
firstMatchedTrans?.uncategorizedTransactionId ||
null
);
}
/**
*Transformes the account transaction to to cashflow transaction node.
* @param {IAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionNode = (transaction: any): ICashflowAccountTransaction => {
const status = this.getTransactionStatus(transaction);
const uncategorizedTransactionId =
this.getUncategorizedTransId(transaction);
return {
date: transaction.date,
formattedDate: moment(transaction.date).format('YYYY-MM-DD'),
withdrawal: transaction.credit,
deposit: transaction.debit,
formattedDeposit: this.formatNumber(transaction.debit),
formattedWithdrawal: this.formatNumber(transaction.credit),
referenceId: transaction.referenceId,
referenceType: transaction.referenceType,
formattedTransactionType: transaction.referenceTypeFormatted,
transactionNumber: transaction.transactionNumber,
referenceNumber: transaction.referenceNumber,
runningBalance: this.runningBalance.amount(),
formattedRunningBalance: this.formatNumber(this.runningBalance.amount()),
balance: 0,
formattedBalance: '',
status,
formattedStatus: formatBankTransactionsStatus(status),
uncategorizedTransactionId,
};
};
/**
* Associate cashflow transaction node with running balance attribute.
* @param {IAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionRunningBalance = (
transaction: ICashflowAccountTransaction,
): ICashflowAccountTransaction => {
const amount = transaction.deposit - transaction.withdrawal;
const biggerThanZero = R.lt(0, amount);
const lowerThanZero = R.gt(0, amount);
const absAmount = Math.abs(amount);
R.when(R.always(biggerThanZero), this.runningBalance.decrement)(absAmount);
R.when(R.always(lowerThanZero), this.runningBalance.increment)(absAmount);
const runningBalance = this.runningBalance.amount();
return {
...transaction,
runningBalance,
formattedRunningBalance: this.formatNumber(runningBalance),
};
};
/**
* Associate to balance attribute to cashflow transaction node.
* @param {ICashflowAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionBalance = (
transaction: ICashflowAccountTransaction,
): ICashflowAccountTransaction => {
const balance =
transaction.runningBalance +
transaction.withdrawal * -1 +
transaction.deposit;
return {
...transaction,
balance,
formattedBalance: this.formatNumber(balance),
};
};
/**
* Transformes the given account transaction to cashflow report transaction.
* @param {ICashflowAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionTransformer = (
transaction,
): ICashflowAccountTransaction => {
return R.compose(
this.transactionBalance,
this.transactionRunningBalance,
this.transactionNode,
)(transaction);
};
/**
* Retrieve the report transactions node.
* @param {} transactions
* @returns {ICashflowAccountTransaction[]}
*/
private transactionsNode = (transactions): ICashflowAccountTransaction[] => {
return R.map(this.transactionTransformer)(transactions);
};
/**
* Retrieve the reprot data node.
* @returns {ICashflowAccountTransaction[]}
*/
public reportData(): ICashflowAccountTransaction[] {
return this.transactionsNode(this.repo.transactions);
}
}

View File

@@ -0,0 +1,122 @@
import { Injectable, Scope } from '@nestjs/common';
import { ICashflowAccountTransactionsQuery } from '../../types/BankingTransactions.types';
import {
groupMatchedBankTransactions,
groupUncategorizedTransactions,
} from './_utils';
@Injectable({ scope: Scope.REQUEST })
export class GetBankAccountTransactionsRepository {
private models: any;
public query: ICashflowAccountTransactionsQuery;
public transactions: any;
public uncategorizedTransactions: any;
public uncategorizedTransactionsMapByRef: Map<string, any>;
public matchedBankTransactions: any;
public matchedBankTransactionsMapByRef: Map<string, any>;
public pagination: any;
public openingBalance: any;
setQuery(query: ICashflowAccountTransactionsQuery) {
this.query = query;
}
/**
* Async initalize the resources.
*/
async asyncInit() {
await this.initCashflowAccountTransactions();
await this.initCashflowAccountOpeningBalance();
await this.initCategorizedTransactions();
await this.initMatchedTransactions();
}
/**
* Retrieve the cashflow account transactions.
* @param {number} tenantId -
* @param {ICashflowAccountTransactionsQuery} query -
*/
async initCashflowAccountTransactions() {
const { AccountTransaction } = this.models;
const { results, pagination } = await AccountTransaction.query()
.where('account_id', this.query.accountId)
.orderBy([
{ column: 'date', order: 'desc' },
{ column: 'created_at', order: 'desc' },
])
.pagination(this.query.page - 1, this.query.pageSize);
this.transactions = results;
this.pagination = pagination;
}
/**
* Retrieve the cashflow account opening balance.
* @param {number} tenantId
* @param {number} accountId
* @param {IPaginationMeta} pagination
* @return {Promise<number>}
*/
async initCashflowAccountOpeningBalance(): Promise<void> {
const { AccountTransaction } = this.models;
// Retrieve the opening balance of credit and debit balances.
const openingBalancesSubquery = AccountTransaction.query()
.where('account_id', this.query.accountId)
.orderBy([
{ column: 'date', order: 'desc' },
{ column: 'created_at', order: 'desc' },
])
.limit(this.pagination.total)
.offset(this.pagination.pageSize * (this.pagination.page - 1));
// Sumation of credit and debit balance.
const openingBalances = await AccountTransaction.query()
.sum('credit as credit')
.sum('debit as debit')
.from(openingBalancesSubquery.as('T'))
.first();
const openingBalance = openingBalances.debit - openingBalances.credit;
this.openingBalance = openingBalance;
}
/**
* Initialize the uncategorized transactions of the bank account.
*/
async initCategorizedTransactions() {
const { UncategorizedCashflowTransaction } = this.models;
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
const uncategorizedTransactions =
await UncategorizedCashflowTransaction.query().whereIn(
['categorizeRefType', 'categorizeRefId'],
refs,
);
this.uncategorizedTransactions = uncategorizedTransactions;
this.uncategorizedTransactionsMapByRef = groupUncategorizedTransactions(
uncategorizedTransactions,
);
}
/**
* Initialize the matched bank transactions of the bank account.
*/
async initMatchedTransactions(): Promise<void> {
const { MatchedBankTransaction } = this.models;
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
const matchedBankTransactions =
await MatchedBankTransaction.query().whereIn(
['referenceType', 'referenceId'],
refs,
);
this.matchedBankTransactions = matchedBankTransactions;
this.matchedBankTransactionsMapByRef = groupMatchedBankTransactions(
matchedBankTransactions,
);
}
}

View File

@@ -0,0 +1,9 @@
export const ERRORS = {
ACCOUNT_ID_HAS_INVALID_TYPE: 'ACCOUNT_ID_HAS_INVALID_TYPE',
};
export enum BankTransactionStatus {
Categorized = 'categorized',
Matched = 'matched',
Manual = 'manual',
}

View File

@@ -0,0 +1,54 @@
import * as R from 'ramda';
export const groupUncategorizedTransactions = (
uncategorizedTransactions: any,
): Map<string, any> => {
return new Map(
R.toPairs(
R.groupBy(
(transaction: any) =>
`${transaction.categorizeRefType}-${transaction.categorizeRefId}`,
uncategorizedTransactions,
),
),
);
};
export const groupMatchedBankTransactions = (
uncategorizedTransactions: any,
): Map<string, any> => {
return new Map(
R.toPairs(
R.groupBy(
(transaction: any) =>
`${transaction.referenceType}-${transaction.referenceId}`,
uncategorizedTransactions,
),
),
);
};
export const formatBankTransactionsStatus = (status) => {
switch (status) {
case 'categorized':
return 'Categorized';
case 'matched':
return 'Matched';
case 'manual':
return 'Manual';
}
};
export const getBankAccountTransactionsDefaultQuery = () => {
return {
pageSize: 50,
page: 1,
numberFormat: {
precision: 2,
divideOn1000: false,
showZero: false,
formatMoney: 'total',
negativeFormat: 'mines',
},
};
};

View File

@@ -2,6 +2,7 @@ import { Knex } from 'knex';
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
import { BankTransaction } from '../models/BankTransaction';
import { CreateBankTransactionDto } from '../dtos/CreateBankTransaction.dto';
import { INumberFormatQuery } from '@/modules/FinancialStatements/types/Report.types';
export interface IPendingTransactionRemovingEventPayload {
uncategorizedTransactionId: number;
@@ -128,3 +129,39 @@ export interface IGetUncategorizedTransactionsQuery {
minAmount?: number;
maxAmount?: number;
}
export interface ICashflowAccountTransactionsQuery {
page: number;
pageSize: number;
accountId: number;
numberFormat: INumberFormatQuery;
}
export interface ICashflowAccountTransaction {
withdrawal: number;
deposit: number;
runningBalance: number;
formattedWithdrawal: string;
formattedDeposit: string;
formattedRunningBalance: string;
transactionNumber: string;
referenceNumber: string;
referenceId: number;
referenceType: string;
formattedTransactionType: string;
balance: number;
formattedBalance: string;
date: Date;
formattedDate: string;
status: string;
formattedStatus: string;
uncategorizedTransactionId: number;
}

View File

@@ -1,3 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { CommandItemCategoryValidatorService } from './CommandItemCategoryValidator.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -7,13 +9,12 @@ import {
IItemCategoryOTD,
} from '../ItemCategory.interfaces';
import { SystemUser } from '@/modules/System/models/SystemUser';
import { Knex } from 'knex';
import { ItemCategory } from '../models/ItemCategory.model';
import { Inject } from '@nestjs/common';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditItemCategoryDto } from '../dtos/ItemCategory.dto';
@Injectable()
export class EditItemCategoryService {
/**
* @param {UnitOfWork} uow - Unit of work.

View File

@@ -17,14 +17,6 @@ class CommandItemCategoryDto {
})
description?: string;
@IsNumber()
@IsNotEmpty()
@ApiProperty({
example: 1,
description: 'The user ID',
})
userId: number;
@IsNumber()
@IsOptional()
@ApiProperty({ example: 1, description: 'The cost account ID' })

View File

@@ -0,0 +1,15 @@
export const runningBalance = (amount: number) => {
let runningBalance = amount;
return {
decrement: (decrement: number) => {
runningBalance -= decrement;
},
increment: (increment: number) => {
runningBalance += increment;
},
amount: () => runningBalance,
};
};

View File

@@ -6,7 +6,7 @@ import t from './types';
// Transform the account.
const transformAccount = (response) => {
return response.data.account;
return response.data;
};
const commonInvalidateQueries = (query) => {
@@ -58,9 +58,9 @@ export function useAccount(id, props) {
export function useAccountsTypes(props) {
return useRequestQuery(
[t.ACCOUNTS_TYPES],
{ method: 'get', url: 'account_types' },
{ method: 'get', url: 'accounts/types' },
{
select: (res) => res.data.account_types,
select: (res) => res.data,
defaultData: [],
...props,
},

View File

@@ -39,7 +39,7 @@ export function useCashflowAccounts(query, props) {
[t.CASH_FLOW_ACCOUNTS, query],
{ method: 'get', url: 'banking/accounts', params: query },
{
select: (res) => res.data.cashflow_accounts,
select: (res) => res.data,
defaultData: [],
...props,
},

View File

@@ -124,7 +124,7 @@ export function useCustomer(id, props) {
[t.CUSTOMER, id],
{ method: 'get', url: `customers/${id}` },
{
select: (res) => res.data.customer,
select: (res) => res.data,
defaultData: {},
...props,
},

View File

@@ -69,7 +69,7 @@ export function useEstimate(id, props) {
[t.SALE_ESTIMATE, id],
{ method: 'get', url: `sale-estimates/${id}` },
{
select: (res) => res.data.estimate,
select: (res) => res.data,
defaultData: {},
...props,
},

View File

@@ -167,7 +167,7 @@ export function useItem(id, props) {
url: `items/${id}`,
},
{
select: (response) => response.data.item,
select: (response) => response.data,
defaultData: {},
...props,
},
@@ -179,10 +179,10 @@ export function useItemAssociatedInvoiceTransactions(id, props) {
[t.ITEM_ASSOCIATED_WITH_INVOICES, id],
{
method: 'get',
url: `items/${id}/transactions/invoices`,
url: `items/${id}/invoices`,
},
{
select: (res) => res.data.data,
select: (res) => res.data,
defaultData: [],
...props,
},
@@ -194,10 +194,10 @@ export function useItemAssociatedEstimateTransactions(id, props) {
[t.ITEM_ASSOCIATED_WITH_ESTIMATES, id],
{
method: 'get',
url: `items/${id}/transactions/estimates`,
url: `items/${id}/estimates`,
},
{
select: (res) => res.data.data,
select: (res) => res.data,
defaultData: [],
...props,
},
@@ -209,10 +209,10 @@ export function useItemAssociatedReceiptTransactions(id, props) {
[t.ITEM_ASSOCIATED_WITH_RECEIPTS, id],
{
method: 'get',
url: `items/${id}/transactions/receipts`,
url: `items/${id}/receipts`,
},
{
select: (res) => res.data.data,
select: (res) => res.data,
defaultData: [],
...props,
},
@@ -223,10 +223,10 @@ export function useItemAssociatedBillTransactions(id, props) {
[t.ITEMS_ASSOCIATED_WITH_BILLS, id],
{
method: 'get',
url: `items/${id}/transactions/bills`,
url: `items/${id}/bills`,
},
{
select: (res) => res.data.data,
select: (res) => res.data,
defaultData: [],
...props,
},
@@ -249,11 +249,11 @@ export function useItemWarehouseLocation(id, props) {
}
/**
*
* @param {*} id
* @param {*} query
* @param {*} props
* @returns
*
* @param {*} id
* @param {*} query
* @param {*} props
* @returns
*/
export function useItemInventoryCost(query, props) {
return useRequestQuery(
@@ -268,5 +268,5 @@ export function useItemInventoryCost(query, props) {
defaultData: [],
...props,
},
);
);
}

View File

@@ -112,7 +112,7 @@ export function useVendor(id, props) {
[t.VENDOR, id],
{ method: 'get', url: `vendors/${id}` },
{
select: (res) => res.data.vendor,
select: (res) => res.data,
defaultData: {},
...props,
},