Merge pull request #112 from bigcapitalhq/BIG-417-fcy-bcy-cashflow-accounts-transactions-api

Add FCY/BCY transactions to the account drawer.
This commit is contained in:
Ahmed Bouhuolia
2023-04-20 06:14:25 +02:00
committed by GitHub
8 changed files with 172 additions and 80 deletions

View File

@@ -177,7 +177,7 @@ export default class ItemsController extends BaseController {
/**
* Validate list query schema.
*/
get validateListQuerySchema() {
private get validateListQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('sort_order').optional().isIn(['desc', 'asc']),
@@ -193,32 +193,20 @@ export default class ItemsController extends BaseController {
];
}
/**
* Validate autocomplete list query schema.
*/
get autocompleteQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('stringified_filter_roles').optional().isJSON(),
query('limit').optional().isNumeric().toInt(),
query('keyword').optional().isString().trim().escape(),
];
}
/**
* Stores the given item details to the storage.
* @param {Request} req
* @param {Response} res
*/
async newItem(req: Request, res: Response, next: NextFunction) {
private async newItem(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const itemDTO: IItemDTO = this.matchedBodyData(req);
try {
const storedItem = await this.itemsApplication.createItem(tenantId, itemDTO);
const storedItem = await this.itemsApplication.createItem(
tenantId,
itemDTO
);
return res.status(200).send({
id: storedItem.id,
@@ -234,7 +222,7 @@ export default class ItemsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
async editItem(req: Request, res: Response, next: NextFunction) {
private async editItem(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const itemId: number = req.params.id;
const item: IItemDTO = this.matchedBodyData(req);
@@ -257,7 +245,7 @@ export default class ItemsController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
async activateItem(req: Request, res: Response, next: NextFunction) {
private async activateItem(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const itemId: number = req.params.id;
@@ -279,7 +267,11 @@ export default class ItemsController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
async inactivateItem(req: Request, res: Response, next: NextFunction) {
private async inactivateItem(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const itemId: number = req.params.id;
@@ -300,7 +292,7 @@ export default class ItemsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
async deleteItem(req: Request, res: Response, next: NextFunction) {
private async deleteItem(req: Request, res: Response, next: NextFunction) {
const itemId: number = req.params.id;
const { tenantId } = req;
@@ -322,7 +314,7 @@ export default class ItemsController extends BaseController {
* @param {Response} res
* @return {Response}
*/
async getItem(req: Request, res: Response, next: NextFunction) {
private async getItem(req: Request, res: Response, next: NextFunction) {
const itemId: number = req.params.id;
const { tenantId } = req;
@@ -342,7 +334,7 @@ export default class ItemsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
async getItemsList(req: Request, res: Response, next: NextFunction) {
private async getItemsList(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {

View File

@@ -42,6 +42,7 @@ export enum AccountNormal {
export interface IAccountsTransactionsFilter {
accountId?: number;
limit?: number;
}
export interface IAccountTransaction {

View File

@@ -106,7 +106,7 @@ export default class AccountTransactionTransformer extends Transformer {
* @returns {string}
*/
protected formattedFcCredit(transaction: IAccountTransaction) {
return this.formatMoney(this.fcDebit(transaction), {
return this.formatMoney(this.fcCredit(transaction), {
currencyCode: transaction.currencyCode,
excerptZero: true,
});
@@ -117,7 +117,7 @@ export default class AccountTransactionTransformer extends Transformer {
* @returns {string}
*/
protected formattedFcDebit(transaction: IAccountTransaction) {
return this.formatMoney(this.fcCredit(transaction), {
return this.formatMoney(this.fcDebit(transaction), {
currencyCode: transaction.currencyCode,
excerptZero: true,
});

View File

@@ -2,7 +2,7 @@
import React from 'react';
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
import { useHistory } from 'react-router-dom';
import { getDashboardRoutes } from '@/routes/dashboard';
import { If, Icon } from '@/components';
import { FormattedMessage as T } from '@/components';
import withDashboard from '@/containers/Dashboard/withDashboard';

View File

@@ -4,23 +4,24 @@ import intl from 'react-intl-universal';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { compose } from '@/utils';
import { TableStyle } from '@/constants';
import { Card, DataTable, If } from '@/components';
import { AccountDrawerTableOptionsProvider } from './AccountDrawerTableOptionsProvider';
import { AccountDrawerTableHeader } from './AccountDrawerTableHeader';
import { useAccountReadEntriesColumns } from './utils';
import { useAppIntlContext } from '@/components/AppIntlProvider';
import { useAccountDrawerContext } from './AccountDrawerProvider';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { compose } from '@/utils';
/**
* account drawer table.
*/
function AccountDrawerTable({ closeDrawer }) {
const { account, accounts, drawerName } = useAccountDrawerContext();
// Account read-only entries table columns.
const columns = useAccountReadEntriesColumns();
const { accounts, drawerName } = useAccountDrawerContext();
// Handle view more link click.
const handleLinkClick = () => {
@@ -31,27 +32,41 @@ function AccountDrawerTable({ closeDrawer }) {
return (
<Card>
<DataTable
columns={columns}
data={accounts}
payload={{ account }}
styleName={TableStyle.Constrant}
/>
<AccountDrawerTableOptionsProvider>
<AccountDrawerTableHeader />
<AccountDrawerDataTable />
<If condition={accounts.length > 0}>
<TableFooter>
<Link
to={`/financial-reports/general-ledger`}
onClick={handleLinkClick}
>
{isRTL ? '→' : '←'} {intl.get('view_more_transactions')}
</Link>
</TableFooter>
</If>
<If condition={accounts.length > 0}>
<TableFooter>
<Link
to={`/financial-reports/general-ledger`}
onClick={handleLinkClick}
>
{isRTL ? '→' : '←'} {intl.get('view_more_transactions')}
</Link>
</TableFooter>
</If>
</AccountDrawerTableOptionsProvider>
</Card>
);
}
function AccountDrawerDataTable() {
const { account, accounts } = useAccountDrawerContext();
// Account read-only entries table columns.
const columns = useAccountReadEntriesColumns();
return (
<DataTable
columns={columns}
data={accounts}
payload={{ account }}
styleName={TableStyle.Constrant}
/>
);
}
export default compose(withDrawerActions)(AccountDrawerTable);
const TableFooter = styled.div`

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { Button, ButtonGroup } from '@blueprintjs/core';
import styled from 'styled-components';
import { useAccountDrawerTableOptionsContext } from './AccountDrawerTableOptionsProvider';
export function AccountDrawerTableHeader() {
const {
setBCYCurrencyType,
setFYCCurrencyType,
isBCYCurrencyType,
isFCYCurrencyType,
} = useAccountDrawerTableOptionsContext();
const handleBCYBtnClick = () => {
setBCYCurrencyType();
};
const handleFCYBtnClick = () => {
setFYCCurrencyType();
};
return (
<TableHeaderRoot>
<ButtonGroup>
<Button
small
outlined
onClick={handleFCYBtnClick}
active={isFCYCurrencyType}
>
FCY
</Button>
<Button
small
outlined
onClick={handleBCYBtnClick}
active={isBCYCurrencyType}
>
BCY
</Button>
</ButtonGroup>
</TableHeaderRoot>
);
}
const TableHeaderRoot = styled.div`
margin-bottom: 1rem;
`;

View File

@@ -0,0 +1,57 @@
import React, { useState, useCallback } from 'react';
interface AccountDrawerTableOptionsContextValue {
setFYCCurrencyType: () => void;
setBCYCurrencyType: () => void;
isFCYCurrencyType: boolean;
isBCYCurrencyType: boolean;
currencyType: ForeignCurrencyType;
}
const AccountDrawerTableOptionsContext = React.createContext(
{} as AccountDrawerTableOptionsContextValue,
);
enum ForeignCurrencyTypes {
FCY = 'FCY',
BCY = 'BCY',
}
type ForeignCurrencyType = ForeignCurrencyTypes.FCY | ForeignCurrencyTypes.BCY;
function AccountDrawerTableOptionsProvider({
initialCurrencyType = ForeignCurrencyTypes.FCY,
...props
}) {
const [currencyType, setCurrentType] =
useState<ForeignCurrencyType>(initialCurrencyType);
const setFYCCurrencyType = useCallback(
() => setCurrentType(ForeignCurrencyTypes.FCY),
[setCurrentType],
);
const setBCYCurrencyType = useCallback(
() => setCurrentType(ForeignCurrencyTypes.BCY),
[setCurrentType],
);
// Provider.
const provider = {
setFYCCurrencyType,
setBCYCurrencyType,
isFCYCurrencyType: currencyType === ForeignCurrencyTypes.FCY,
isBCYCurrencyType: currencyType === ForeignCurrencyTypes.BCY,
currencyType,
};
return (
<AccountDrawerTableOptionsContext.Provider value={provider} {...props} />
);
}
const useAccountDrawerTableOptionsContext = () =>
React.useContext(AccountDrawerTableOptionsContext);
export {
AccountDrawerTableOptionsProvider,
useAccountDrawerTableOptionsContext,
};

View File

@@ -3,27 +3,15 @@ import intl from 'react-intl-universal';
import React from 'react';
import { FormatDateCell } from '@/components';
import { isBlank } from '@/utils';
/**
* Debit/credit table cell.
*/
function DebitCreditTableCell({ value, payload: { account } }) {
return !isBlank(value) && value !== 0 ? account.formatted_amount : null;
}
/**
* Running balance table cell.
*/
function RunningBalanceTableCell({ value, payload: { account } }) {
return account.formatted_amount;
}
import { useAccountDrawerTableOptionsContext } from './AccountDrawerTableOptionsProvider';
/**
* Retrieve entries columns of read-only account view.
*/
export const useAccountReadEntriesColumns = () =>
React.useMemo(
export const useAccountReadEntriesColumns = () => {
const { isFCYCurrencyType } = useAccountDrawerTableOptionsContext();
return React.useMemo(
() => [
{
Header: intl.get('transaction_date'),
@@ -34,14 +22,15 @@ export const useAccountReadEntriesColumns = () =>
},
{
Header: intl.get('transaction_type'),
accessor: 'reference_type_formatted',
accessor: 'transaction_type_formatted',
width: 100,
textOverview: true,
},
{
Header: intl.get('credit'),
accessor: 'credit',
Cell: DebitCreditTableCell,
accessor: isFCYCurrencyType
? 'formatted_fc_credit'
: 'formatted_credit',
width: 80,
className: 'credit',
align: 'right',
@@ -49,22 +38,13 @@ export const useAccountReadEntriesColumns = () =>
},
{
Header: intl.get('debit'),
accessor: 'debit',
Cell: DebitCreditTableCell,
accessor: isFCYCurrencyType ? 'formatted_fc_debit' : 'formatted_debit',
width: 80,
className: 'debit',
align: 'right',
textOverview: true,
},
{
Header: intl.get('running_balance'),
Cell: RunningBalanceTableCell,
accessor: 'running_balance',
width: 110,
className: 'running_balance',
align: 'right',
textOverview: true,
},
],
[],
[isFCYCurrencyType],
);
};