mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import R from 'ramda';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
ICashflowAccountTransaction,
|
||||
ICashflowAccountTransactionsQuery,
|
||||
INumberFormatQuery,
|
||||
} from '@/interfaces';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { runningAmount } from 'utils';
|
||||
|
||||
export default class CashflowAccountTransactionReport extends FinancialSheet {
|
||||
private transactions: any;
|
||||
private openingBalance: number;
|
||||
private runningBalance: any;
|
||||
private numberFormat: INumberFormatQuery;
|
||||
private baseCurrency: string;
|
||||
private query: ICashflowAccountTransactionsQuery;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IAccountTransaction[]} transactions -
|
||||
* @param {number} openingBalance -
|
||||
* @param {ICashflowAccountTransactionsQuery} query -
|
||||
*/
|
||||
constructor(
|
||||
transactions,
|
||||
openingBalance: number,
|
||||
query: ICashflowAccountTransactionsQuery
|
||||
) {
|
||||
super();
|
||||
|
||||
this.transactions = transactions;
|
||||
this.openingBalance = openingBalance;
|
||||
|
||||
this.runningBalance = runningAmount(this.openingBalance);
|
||||
this.query = query;
|
||||
this.numberFormat = query.numberFormat;
|
||||
this.baseCurrency = 'USD';
|
||||
}
|
||||
|
||||
/**
|
||||
*Transformes the account transaction to to cashflow transaction node.
|
||||
* @param {IAccountTransaction} transaction
|
||||
* @returns {ICashflowAccountTransaction}
|
||||
*/
|
||||
private transactionNode = (transaction: any): ICashflowAccountTransaction => {
|
||||
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: '',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.transactions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ICashflowAccountTransactionsQuery, IPaginationMeta } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class CashflowAccountTransactionsRepo {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account transactions.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICashflowAccountTransactionsQuery} query -
|
||||
*/
|
||||
async getCashflowAccountTransactions(
|
||||
tenantId: number,
|
||||
query: ICashflowAccountTransactionsQuery
|
||||
) {
|
||||
const { AccountTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
return AccountTransaction.query()
|
||||
.where('account_id', query.accountId)
|
||||
.orderBy([
|
||||
{ column: 'date', order: 'desc' },
|
||||
{ column: 'created_at', order: 'desc' },
|
||||
])
|
||||
.pagination(query.page - 1, query.pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account opening balance.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @param {IPaginationMeta} pagination
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
async getCashflowAccountOpeningBalance(
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
pagination: IPaginationMeta
|
||||
): Promise<number> {
|
||||
const { AccountTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the opening balance of credit and debit balances.
|
||||
const openingBalancesSubquery = AccountTransaction.query()
|
||||
.where('account_id', accountId)
|
||||
.orderBy([
|
||||
{ column: 'date', order: 'desc' },
|
||||
{ column: 'created_at', order: 'desc' },
|
||||
])
|
||||
.limit(pagination.total)
|
||||
.offset(pagination.pageSize * (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;
|
||||
|
||||
return openingBalance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { includes } from 'lodash';
|
||||
import * as qim from 'qim';
|
||||
import { ICashflowAccountTransactionsQuery, IAccount } from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import CashflowAccountTransactionsRepo from './CashflowAccountTransactionsRepo';
|
||||
import CashflowAccountTransactionsReport from './CashflowAccountTransactions';
|
||||
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
import I18nService from '@/services/I18n/I18nService';
|
||||
|
||||
@Service()
|
||||
export default class CashflowAccountTransactionsService extends FinancialSheet {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
cashflowTransactionsRepo: CashflowAccountTransactionsRepo;
|
||||
|
||||
@Inject()
|
||||
i18nService: I18nService;
|
||||
|
||||
/**
|
||||
* Defaults balance sheet filter query.
|
||||
* @return {IBalanceSheetQuery}
|
||||
*/
|
||||
private get defaultQuery(): Partial<ICashflowAccountTransactionsQuery> {
|
||||
return {
|
||||
pageSize: 50,
|
||||
page: 1,
|
||||
numberFormat: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'mines',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow accouynt transactions report data.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICashflowAccountTransactionsQuery} query -
|
||||
* @return {Promise<IInvetoryItemDetailDOO>}
|
||||
*/
|
||||
public async cashflowAccountTransactions(
|
||||
tenantId: number,
|
||||
query: ICashflowAccountTransactionsQuery
|
||||
) {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
const parsedQuery = { ...this.defaultQuery, ...query };
|
||||
|
||||
// Retrieve the given account or throw not found service error.
|
||||
const account = await Account.query().findById(parsedQuery.accountId);
|
||||
|
||||
// Validates the cashflow account type.
|
||||
this.validateCashflowAccountType(account);
|
||||
|
||||
// Retrieve the cashflow account transactions.
|
||||
const { results: transactions, pagination } =
|
||||
await this.cashflowTransactionsRepo.getCashflowAccountTransactions(
|
||||
tenantId,
|
||||
parsedQuery
|
||||
);
|
||||
// Retrieve the cashflow account opening balance.
|
||||
const openingBalance =
|
||||
await this.cashflowTransactionsRepo.getCashflowAccountOpeningBalance(
|
||||
tenantId,
|
||||
parsedQuery.accountId,
|
||||
pagination
|
||||
);
|
||||
// Retrieve the computed report.
|
||||
const report = new CashflowAccountTransactionsReport(
|
||||
transactions,
|
||||
openingBalance,
|
||||
parsedQuery
|
||||
);
|
||||
const reportTranasctions = report.reportData();
|
||||
|
||||
return {
|
||||
transactions: this.i18nService.i18nApply(
|
||||
[[qim.$each, 'formattedTransactionType']],
|
||||
reportTranasctions,
|
||||
tenantId
|
||||
),
|
||||
pagination,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the cashflow account type.
|
||||
* @param {IAccount} account -
|
||||
*/
|
||||
private validateCashflowAccountType(account: IAccount) {
|
||||
const cashflowTypes = [
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.CREDIT_CARD,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
];
|
||||
|
||||
if (!includes(cashflowTypes, account.accountType)) {
|
||||
throw new ServiceError(ERRORS.ACCOUNT_ID_HAS_INVALID_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export const ERRORS = {
|
||||
ACCOUNT_ID_HAS_INVALID_TYPE: 'ACCOUNT_ID_HAS_INVALID_TYPE',
|
||||
};
|
||||
Reference in New Issue
Block a user