mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
Merge branch 'develop'
This commit is contained in:
@@ -25,7 +25,7 @@ export default function AccountDrawerHeader() {
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'account-normal'} label={<T id={'account_normal'} />}>
|
||||
{account.account_normal}
|
||||
{account.account_normal_formatted}
|
||||
<Icon
|
||||
iconSize={14}
|
||||
icon={`arrow-${
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { useAccountDrawerContext } from './AccountDrawerProvider';
|
||||
|
||||
import { DataTable, If } from 'components';
|
||||
import { Card, DataTable, If } from 'components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useAccountReadEntriesColumns } from './utils';
|
||||
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { TableStyle } from '../../../common';
|
||||
import { useAppIntlContext } from 'components/AppIntlProvider';
|
||||
|
||||
/**
|
||||
* account drawer table.
|
||||
*/
|
||||
function AccountDrawerTable({ closeDrawer }) {
|
||||
const {
|
||||
account,
|
||||
accounts,
|
||||
drawerName,
|
||||
} = useAccountDrawerContext();
|
||||
const { account, accounts, drawerName } = useAccountDrawerContext();
|
||||
|
||||
// Account read-only entries table columns.
|
||||
const columns = useAccountReadEntriesColumns();
|
||||
@@ -29,23 +27,38 @@ function AccountDrawerTable({ closeDrawer }) {
|
||||
const handleLinkClick = () => {
|
||||
closeDrawer(drawerName);
|
||||
};
|
||||
// Application intl context.
|
||||
const { isRTL } = useAppIntlContext();
|
||||
|
||||
return (
|
||||
<div className={'account-drawer__table'}>
|
||||
<DataTable columns={columns} data={accounts} payload={{ account }}/>
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
payload={{ account }}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
|
||||
<If condition={accounts.length > 0}>
|
||||
<div class="account-drawer__table-footer">
|
||||
<TableFooter>
|
||||
<Link
|
||||
to={`/financial-reports/general-ledger`}
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
←{intl.get('view_more_transactions')}
|
||||
{isRTL ? '→' : '←'} {intl.get('view_more_transactions')}
|
||||
</Link>
|
||||
</div>
|
||||
</TableFooter>
|
||||
</If>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDrawerActions)(AccountDrawerTable);
|
||||
|
||||
const TableFooter = styled.div`
|
||||
padding: 6px 14px;
|
||||
display: block;
|
||||
border-top: 1px solid #d2dde2;
|
||||
border-bottom: 1px solid #d2dde2;
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
@@ -16,12 +15,19 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { Can, If, Icon, FormattedMessage as T } from 'components';
|
||||
import {
|
||||
Can,
|
||||
If,
|
||||
Icon,
|
||||
DrawerActionsBar,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
import {
|
||||
BillAction,
|
||||
PaymentMadeAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
import { BillMenuItem } from './utils';
|
||||
|
||||
import { safeCallback, compose } from 'utils';
|
||||
|
||||
@@ -45,6 +51,14 @@ function BillDetailActionsBar({
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
// Handle convert to vendor credit.
|
||||
const handleConvertToVendorCredit = () => {
|
||||
history.push(`/vendor-credits/new?from_bill_id=${billId}`, {
|
||||
billId: billId,
|
||||
});
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
// Handle delete bill.
|
||||
const onDeleteBill = () => {
|
||||
openAlert('bill-delete', { billId });
|
||||
@@ -55,8 +69,13 @@ function BillDetailActionsBar({
|
||||
openDialog('quick-payment-made', { billId });
|
||||
};
|
||||
|
||||
// Handle allocate landed cost button click.
|
||||
const handleAllocateCostClick = () => {
|
||||
openDialog('allocate-landed-cost', { billId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<Button
|
||||
@@ -71,7 +90,7 @@ function BillDetailActionsBar({
|
||||
<If condition={bill.is_open && !bill.is_fully_paid}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
||||
icon={<Icon icon="arrow-upward" iconSize={16} />}
|
||||
text={<T id={'add_payment'} />}
|
||||
onClick={handleQuickBillPayment}
|
||||
/>
|
||||
@@ -87,8 +106,17 @@ function BillDetailActionsBar({
|
||||
onClick={safeCallback(onDeleteBill)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<NavbarDivider />
|
||||
<BillMenuItem
|
||||
payload={{
|
||||
onConvert: handleConvertToVendorCredit,
|
||||
onAllocateLandedCost: handleAllocateCostClick,
|
||||
}}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import {
|
||||
CommercialDocFooter,
|
||||
T,
|
||||
If,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
} from 'components';
|
||||
|
||||
import { FormatNumber, T, TotalLines, TotalLine } from '../../../components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
|
||||
|
||||
/**
|
||||
* Bill read-only details footer.
|
||||
* Bill detail footer.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function BillDetailFooter() {
|
||||
export default function BillDetailFooter() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(BillDrawerCls.detail_panel_footer)}>
|
||||
<TotalLines>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.subtotal'} />}
|
||||
value={<FormatNumber value={bill.amount} />}
|
||||
className={BillDrawerCls.total_line_subtotal}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.total'} />}
|
||||
value={bill.formatted_amount}
|
||||
className={BillDrawerCls.total_line_total}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.payment_amount'} />}
|
||||
value={bill.formatted_payment_amount}
|
||||
className={BillDrawerCls.total_line_payment}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.due_amount'} />}
|
||||
value={bill.formatted_due_amount}
|
||||
className={BillDrawerCls.total_line_dueAmount}
|
||||
/>
|
||||
</TotalLines>
|
||||
</div>
|
||||
<CommercialDocFooter>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<If condition={bill.note}>
|
||||
<DetailItem label={<T id={'note'} />}>{bill.note}</DetailItem>
|
||||
</If>
|
||||
</DetailsMenu>
|
||||
</CommercialDocFooter>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { defaultTo } from 'lodash';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { FormatDate, DetailsMenu, DetailItem } from 'components';
|
||||
import {
|
||||
FormatDate,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
ButtonLink,
|
||||
Row,
|
||||
Col,
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
VendorDrawerLink,
|
||||
} from 'components';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
|
||||
import { BillDetailsStatus } from './utils';
|
||||
|
||||
/**
|
||||
* Bill detail header.
|
||||
@@ -15,44 +25,67 @@ export default function BillDetailHeader() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(BillDrawerCls.detail_panel_header)}>
|
||||
<DetailsMenu>
|
||||
<DetailItem
|
||||
label={intl.get('amount')}
|
||||
children={<h3 class="big-number">{bill.formatted_amount}</h3>}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('bill.details.bill_number')}
|
||||
children={defaultTo(bill.bill_number, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('bill_date')}
|
||||
children={<FormatDate value={bill.bill_date} />}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('vendor_name')}
|
||||
children={bill.vendor?.display_name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('due_date')}
|
||||
children={<FormatDate value={bill.due_date} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'140px'}>
|
||||
<DetailItem
|
||||
label={intl.get('due_amount')}
|
||||
children={bill.formatted_due_amount}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(bill.reference_no, '--')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('bill.details.created_at')}
|
||||
children={<FormatDate value={bill.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<AmountDetailItem label={intl.get('amount')}>
|
||||
<h3 class="big-number">{bill.formatted_amount}</h3>
|
||||
</AmountDetailItem>
|
||||
<StatusDetailItem>
|
||||
<BillDetailsStatus bill={bill} />
|
||||
</StatusDetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem label={intl.get('bill_date')}>
|
||||
<FormatDate value={bill.bill_date} />
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('due_date')}>
|
||||
<FormatDate value={bill.due_date} />
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('vendor_name')}>
|
||||
<VendorDrawerLink vendorId={bill.vendor_id}>
|
||||
{bill.vendor?.display_name}
|
||||
</VendorDrawerLink>
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('bill.details.bill_number')}>
|
||||
{defaultTo(bill.bill_number, '-')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'140px'}
|
||||
textAlign={'right'}
|
||||
>
|
||||
<DetailItem label={intl.get('due_amount')}>
|
||||
<strong>{bill.formatted_due_amount}</strong>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(bill.reference_no, '--')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('bill.details.created_at')}
|
||||
children={<FormatDate value={bill.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
`;
|
||||
|
||||
const AmountDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
`;
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { CommercialDocBox } from 'components';
|
||||
|
||||
import BillDetailActionsBar from './BillDetailActionsBar';
|
||||
import BillDetailHeader from './BillDetailHeader';
|
||||
import BillDetailTable from './BillDetailTable';
|
||||
import { BillDetailFooter } from './BillDetailFooter'
|
||||
|
||||
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
|
||||
|
||||
import { BillDetailTableFooter } from './BillDetailTableFooter';
|
||||
import BillDetailFooter from './BillDetailFooter';
|
||||
|
||||
/**
|
||||
* Bill detail panel tab.
|
||||
*/
|
||||
export default function BillDetailTab() {
|
||||
return (
|
||||
<div className={clsx(BillDrawerCls.detail_panel)}>
|
||||
<BillDetailActionsBar />
|
||||
|
||||
<Card>
|
||||
<BillDetailHeader />
|
||||
<BillDetailTable />
|
||||
<BillDetailFooter />
|
||||
</Card>
|
||||
</div>
|
||||
<CommercialDocBox>
|
||||
<BillDetailHeader />
|
||||
<BillDetailTable />
|
||||
<BillDetailTableFooter />
|
||||
<BillDetailFooter />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { DataTable } from 'components';
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
import { useBillReadonlyEntriesTableColumns } from './utils';
|
||||
|
||||
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
|
||||
import { TableStyle } from '../../../common';
|
||||
|
||||
export default function BillDetailTable() {
|
||||
const { bill: { entries } } = useBillDrawerContext();
|
||||
const {
|
||||
bill: { entries },
|
||||
} = useBillDrawerContext();
|
||||
|
||||
// Retrieve bill readonly entries table columns.
|
||||
const columns = useBillReadonlyEntriesTableColumns();
|
||||
|
||||
return (
|
||||
<div className={clsx(BillDrawerCls.detail_panel_table)}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
</div>
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
51
src/containers/Drawers/BillDrawer/BillDetailTableFooter.js
Normal file
51
src/containers/Drawers/BillDrawer/BillDetailTableFooter.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
FormatNumber,
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
} from '../../../components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
/**
|
||||
* Bill read-only details table footer.
|
||||
*/
|
||||
export function BillDetailTableFooter() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
|
||||
return (
|
||||
<BillDetailsFooterRoot>
|
||||
<BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.subtotal'} />}
|
||||
value={<FormatNumber value={bill.amont} />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.total'} />}
|
||||
value={bill.formatted_amount}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.payment_amount'} />}
|
||||
value={bill.formatted_payment_amount}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.due_amount'} />}
|
||||
value={bill.formatted_due_amount}
|
||||
/>
|
||||
</BillTotalLines>
|
||||
</BillDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillDetailsFooterRoot = styled.div``;
|
||||
|
||||
export const BillTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
@@ -1,13 +0,0 @@
|
||||
import React from 'react';
|
||||
import BillLocatedLandedCostDeleteAlert from 'containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert';
|
||||
|
||||
/**
|
||||
* Bill drawer alert.
|
||||
*/
|
||||
export default function BillDrawerAlerts() {
|
||||
return (
|
||||
<div class="bills-alerts">
|
||||
<BillLocatedLandedCostDeleteAlert name="bill-located-cost-delete" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import React from 'react';
|
||||
import { DrawerBody } from 'components';
|
||||
|
||||
import 'style/components/Drawers/ViewDetail/ViewDetail.scss';
|
||||
|
||||
import { BillDrawerProvider } from './BillDrawerProvider';
|
||||
import BillDrawerDetails from './BillDrawerDetails';
|
||||
import BillDrawerAlerts from './BillDrawerAlerts';
|
||||
|
||||
/**
|
||||
* Bill drawer content.
|
||||
@@ -18,7 +15,6 @@ export default function BillDrawerContent({
|
||||
<BillDrawerProvider billId={billId}>
|
||||
<DrawerBody>
|
||||
<BillDrawerDetails />
|
||||
<BillDrawerAlerts />
|
||||
</DrawerBody>
|
||||
</BillDrawerProvider>
|
||||
);
|
||||
|
||||
@@ -1,49 +1,67 @@
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DrawerMainTabs } from 'components';
|
||||
|
||||
import {
|
||||
PaymentMadeAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
import { useAbilityContext } from 'hooks/utils';
|
||||
import BillDetailTab from './BillDetailTab';
|
||||
import LocatedLandedCostTable from './LocatedLandedCostTable';
|
||||
import JournalEntriesTable from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
|
||||
import BillGLEntriesTable from './BillGLEntriesTable';
|
||||
import BillPaymentTransactionTable from './BillPaymentTransactions/BillPaymentTransactionTable';
|
||||
import BillDetailActionsBar from './BillDetailActionsBar';
|
||||
|
||||
/**
|
||||
* Bill view details.
|
||||
* Bill details tabs.
|
||||
*/
|
||||
export default function BillDrawerDetails() {
|
||||
const {
|
||||
data: { transactions },
|
||||
} = useBillDrawerContext();
|
||||
function BillDetailsTabs() {
|
||||
const ability = useAbilityContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(BillDrawerCls.root)}>
|
||||
<DrawerMainTabs defaultSelectedTabId="details">
|
||||
<DrawerMainTabs
|
||||
renderActiveTabPanelOnly={true}
|
||||
defaultSelectedTabId="details"
|
||||
>
|
||||
<Tab
|
||||
title={intl.get('overview')}
|
||||
id={'details'}
|
||||
panel={<BillDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<BillGLEntriesTable />}
|
||||
/>
|
||||
{ability.can(PaymentMadeAction.View, AbilitySubject.PaymentMade) && (
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<BillDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<JournalEntriesTable transactions={transactions} />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('located_landed_cost')}
|
||||
id={'landed_cost'}
|
||||
panel={<LocatedLandedCostTable />}
|
||||
/>
|
||||
{/* <Tab
|
||||
title={intl.get('payment_transactions')}
|
||||
id={'payment_transactions'}
|
||||
// panel={}
|
||||
/> */}
|
||||
</DrawerMainTabs>
|
||||
</div>
|
||||
panel={<BillPaymentTransactionTable />}
|
||||
/>
|
||||
)}
|
||||
<Tab
|
||||
title={intl.get('located_landed_cost')}
|
||||
id={'landed_cost'}
|
||||
panel={<LocatedLandedCostTable />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bill view detail.
|
||||
*/
|
||||
export default function BillDetails() {
|
||||
return (
|
||||
<BillDetailsRoot>
|
||||
<BillDetailActionsBar />
|
||||
<BillDetailsTabs />
|
||||
</BillDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillDetailsRoot = styled.div``;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
||||
import {
|
||||
useBill,
|
||||
useTransactionsByReference,
|
||||
useBillLocatedLandedCost,
|
||||
} from 'hooks/query';
|
||||
import { useBill, useBillLocatedLandedCost } from 'hooks/query';
|
||||
|
||||
const BillDrawerContext = React.createContext();
|
||||
|
||||
@@ -18,15 +14,6 @@ function BillDrawerProvider({ billId, ...props }) {
|
||||
enabled: !!billId,
|
||||
});
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const { data, isLoading: isTransactionLoading } = useTransactionsByReference(
|
||||
{
|
||||
reference_id: billId,
|
||||
reference_type: 'Bill',
|
||||
},
|
||||
{ enabled: !!billId },
|
||||
);
|
||||
|
||||
// Handle fetch bill located landed cost transaction.
|
||||
const { isLoading: isLandedCostLoading, data: transactions } =
|
||||
useBillLocatedLandedCost(billId, {
|
||||
@@ -35,19 +22,20 @@ function BillDrawerProvider({ billId, ...props }) {
|
||||
|
||||
//provider.
|
||||
const provider = {
|
||||
transactions,
|
||||
billId,
|
||||
data,
|
||||
transactions,
|
||||
bill,
|
||||
};
|
||||
|
||||
const loading = isLandedCostLoading || isTransactionLoading || isBillLoading;
|
||||
const loading = isLandedCostLoading || isBillLoading;
|
||||
|
||||
return (
|
||||
<DrawerLoading loading={loading}>
|
||||
<DrawerHeaderContent
|
||||
name="bill-drawer"
|
||||
title={intl.get('bill_details')}
|
||||
title={intl.get('bill.drawer.title', {
|
||||
number: bill.bill_number ? `(${bill.bill_number})` : null,
|
||||
})}
|
||||
/>
|
||||
<BillDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
|
||||
49
src/containers/Drawers/BillDrawer/BillGLEntriesTable.js
Normal file
49
src/containers/Drawers/BillDrawer/BillGLEntriesTable.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
|
||||
import { useTransactionsByReference } from 'hooks/query';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import JournalEntriesTable, {
|
||||
AmountDisplayedBaseCurrencyMessage,
|
||||
} from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
|
||||
/**
|
||||
* Bill GL entries table.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function BillGLEntriesTable() {
|
||||
const { billId } = useBillDrawerContext();
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: billId,
|
||||
reference_type: 'Bill',
|
||||
},
|
||||
{ enabled: !!billId },
|
||||
);
|
||||
|
||||
return (
|
||||
<BilleGLEntriesRoot>
|
||||
<AmountDisplayedBaseCurrencyMessage />
|
||||
<BillGLEntriesDatatable
|
||||
loading={isTransactionLoading}
|
||||
transactions={transactions}
|
||||
/>
|
||||
</BilleGLEntriesRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const BilleGLEntriesRoot = styled(Card)``;
|
||||
|
||||
const BillGLEntriesDatatable = styled(JournalEntriesTable)`
|
||||
.table .tbody .tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { DataTable, Card } from 'components';
|
||||
|
||||
import { useBillPaymentTransactionsColumns, ActionsMenu } from './components';
|
||||
import { useBillDrawerContext } from '../BillDrawerProvider';
|
||||
import { useBillPaymentTransactions } from 'hooks/query';
|
||||
|
||||
import { TableStyle } from '../../../../common';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bill payment transactions datatable.
|
||||
*/
|
||||
function BillPaymentTransactionTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useBillPaymentTransactionsColumns();
|
||||
|
||||
const { billId } = useBillDrawerContext();
|
||||
|
||||
// Handle fetch bill payment transaction.
|
||||
const {
|
||||
isLoading: isPaymentTransactionsLoading,
|
||||
isFetching: isPaymentTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useBillPaymentTransactions(billId, {
|
||||
enabled: !!billId,
|
||||
});
|
||||
|
||||
// Handles delete bill payment transactions.
|
||||
const handleDeleteBillPaymentTransactons = ({ bill_payment_id }) => {
|
||||
openAlert('payment-made-delete', {
|
||||
paymentMadeId: bill_payment_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit bill payment transactions.
|
||||
const handleEditBillPaymentTransactions = ({ bill_payment_id }) => {
|
||||
history.push(`/payment-mades/${bill_payment_id}/edit`);
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isPaymentTransactionsLoading}
|
||||
headerLoading={isPaymentTransactionsLoading}
|
||||
progressBarLoading={isPaymentTransactionFetching}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteBillPaymentTransactons,
|
||||
onEdit: handleEditBillPaymentTransactions,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(BillPaymentTransactionTable);
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../common/classes';
|
||||
import { Can, FormatDateCell, Icon } from '../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
PaymentMadeAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={PaymentMadeAction.Edit} a={AbilitySubject.PaymentMade}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={PaymentMadeAction.Delete} a={AbilitySubject.PaymentMade}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bill payment transactions table columns.
|
||||
*/
|
||||
export const useBillPaymentTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'date',
|
||||
Header: intl.get('payment_date'),
|
||||
accessor: 'formatted_payment_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 110,
|
||||
className: 'date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_account_name',
|
||||
Header: intl.get('bill_transactions.column.deposit_account'),
|
||||
accessor: 'payment_account_name',
|
||||
width: 120,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_payment_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_number',
|
||||
Header: intl.get('payment_no'),
|
||||
accessor: 'payment_number',
|
||||
width: 100,
|
||||
className: 'payment_number',
|
||||
},
|
||||
{
|
||||
id: 'reference',
|
||||
Header: intl.get('reference_no'),
|
||||
accessor: 'payment_reference_no',
|
||||
width: 90,
|
||||
className: 'payment_reference_no',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { DataTable, Card } from 'components';
|
||||
import { Button, Classes, NavbarGroup } from '@blueprintjs/core';
|
||||
import { DataTable, Card, FormattedMessage as T } from 'components';
|
||||
|
||||
import { useLocatedLandedCostColumns, ActionsMenu } from './components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
@@ -9,9 +8,11 @@ import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { TableStyle } from '../../../common';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import Icon from 'components/Icon';
|
||||
|
||||
|
||||
/**
|
||||
* Located landed cost table.
|
||||
@@ -26,7 +27,10 @@ function LocatedLandedCostTable({
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
// Located landed cost table columns.
|
||||
const columns = useLocatedLandedCostColumns();
|
||||
|
||||
// Bill drawer context.
|
||||
const { transactions, billId } = useBillDrawerContext();
|
||||
|
||||
// Handle the transaction delete action.
|
||||
@@ -34,11 +38,6 @@ function LocatedLandedCostTable({
|
||||
openAlert('bill-located-cost-delete', { BillId: id });
|
||||
};
|
||||
|
||||
// Handle allocate landed cost button click.
|
||||
const handleAllocateCostClick = () => {
|
||||
openDialog('allocate-landed-cost', { billId });
|
||||
};
|
||||
|
||||
// Handle from transaction link click.
|
||||
const handleFromTransactionClick = (original) => {
|
||||
const { from_transaction_type, from_transaction_id } = original;
|
||||
@@ -57,27 +56,17 @@ function LocatedLandedCostTable({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="receipt-24" />}
|
||||
text={'Allocate landed cost'}
|
||||
onClick={handleAllocateCostClick}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={transactions}
|
||||
ContextMenu={ActionsMenu}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
payload={{
|
||||
onDelete: handleDeleteTransaction,
|
||||
onFromTranscationClick: handleFromTransactionClick,
|
||||
}}
|
||||
className={'datatable--landed-cost-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import clsx from 'classnames';
|
||||
import { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||
import { safeCallback } from 'utils';
|
||||
import { Icon } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
@@ -24,7 +28,7 @@ export function ActionsMenu({ row: { original }, payload: { onDelete } }) {
|
||||
*/
|
||||
export function FromTransactionCell({
|
||||
row: { original },
|
||||
payload: { onFromTranscationClick }
|
||||
payload: { onFromTranscationClick },
|
||||
}) {
|
||||
// Handle the link click
|
||||
const handleAnchorClick = () => {
|
||||
@@ -38,6 +42,18 @@ export function FromTransactionCell({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name accessor.
|
||||
*/
|
||||
export const NameAccessor = (row) => {
|
||||
return (
|
||||
<span className="name">
|
||||
<LabelName>{row.name}</LabelName>
|
||||
<LabelDescription>{row.description}</LabelDescription>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve bill located landed cost table columns.
|
||||
*/
|
||||
@@ -46,30 +62,41 @@ export function useLocatedLandedCostColumns() {
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('name'),
|
||||
accessor: 'description',
|
||||
accessor: NameAccessor,
|
||||
width: 150,
|
||||
className: 'name',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_amount',
|
||||
width: 100,
|
||||
className: 'amount',
|
||||
align: 'right',
|
||||
textOverview: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
id: 'from_transaction',
|
||||
Header: intl.get('From transaction'),
|
||||
Cell: FromTransactionCell,
|
||||
width: 100,
|
||||
className: 'from-transaction',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('allocation_method'),
|
||||
accessor: 'allocation_method_formatted',
|
||||
width: 100,
|
||||
className: 'allocation-method',
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const LabelName = styled.div``;
|
||||
|
||||
const LabelDescription = styled.div`
|
||||
font-size: 12px;
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
opacity: 0.75;
|
||||
`;
|
||||
|
||||
@@ -1,51 +1,136 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { FormatNumberCell } from '../../../components';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Intent,
|
||||
Tag,
|
||||
} from '@blueprintjs/core';
|
||||
import {
|
||||
FormatNumberCell,
|
||||
FormattedMessage as T,
|
||||
Choose,
|
||||
Icon,
|
||||
} from '../../../components';
|
||||
|
||||
/**
|
||||
* Retrieve bill readonly details entries table columns.
|
||||
*/
|
||||
export const useBillReadonlyEntriesTableColumns = () =>
|
||||
React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('product_and_service'),
|
||||
accessor: 'item.name',
|
||||
width: 150,
|
||||
className: 'item',
|
||||
disableSortBy: true
|
||||
},
|
||||
{
|
||||
Header: intl.get('description'),
|
||||
accessor: 'description',
|
||||
className: 'description',
|
||||
disableSortBy: true
|
||||
},
|
||||
{
|
||||
Header: intl.get('quantity'),
|
||||
accessor: 'quantity',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true
|
||||
},
|
||||
{
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'rate',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('product_and_service'),
|
||||
accessor: 'item.name',
|
||||
width: 150,
|
||||
className: 'item',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('description'),
|
||||
accessor: 'description',
|
||||
className: 'description',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('quantity'),
|
||||
accessor: 'quantity',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'rate',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Bill details status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function BillDetailsStatus({ bill }) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={bill.is_fully_paid && bill.is_open}>
|
||||
<StatusTag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'paid'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={bill.is_open}>
|
||||
<Choose>
|
||||
<Choose.When condition={bill.is_overdue}>
|
||||
<StatusTag intent={Intent.WARNING} round={true}>
|
||||
<T id={'overdue'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag intent={Intent.PRIMARY} round={true}>
|
||||
<T id={'due'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag round={true} minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillMenuItem = ({
|
||||
payload: { onConvert, onAllocateLandedCost },
|
||||
}) => {
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={onAllocateLandedCost}
|
||||
text={<T id={'bill.allocate_landed_coast'} />}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={onConvert}
|
||||
text={<T id={'bill.convert_to_credit_note'} />}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button icon={<Icon icon="more-vert" iconSize={16} />} minimal={true} />
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
const StatusTag = styled(Tag)`
|
||||
min-width: 65px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import { Button, Classes, NavbarGroup, Intent } from '@blueprintjs/core';
|
||||
import { Can, FormattedMessage as T } from 'components';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import { Can, FormattedMessage as T, DrawerActionsBar } from 'components';
|
||||
import { useCashflowTransactionDrawerContext } from './CashflowTransactionDrawerProvider';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import { AbilitySubject, CashflowAction } from '../../../common/abilityOption';
|
||||
@@ -25,7 +24,7 @@ function CashflowTransactionDrawerActionBar({
|
||||
|
||||
return (
|
||||
<Can I={CashflowAction.Delete} a={AbilitySubject.Cashflow}>
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
@@ -35,8 +34,9 @@ function CashflowTransactionDrawerActionBar({
|
||||
onClick={handleDeleteCashflowTransaction}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
</Can>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(CashflowTransactionDrawerActionBar);
|
||||
|
||||
@@ -13,6 +13,7 @@ export default function CashflowTransactionDrawerDetails() {
|
||||
return (
|
||||
<div className={'cashflow-drawer'}>
|
||||
<CashflowTransactionDrawerActionBar />
|
||||
|
||||
<div className="cashflow-drawer__content">
|
||||
<Card>
|
||||
<CashflowTransactionDrawerHeader />
|
||||
|
||||
@@ -5,6 +5,9 @@ import {
|
||||
DetailItem,
|
||||
FormatDate,
|
||||
FormattedMessage as T,
|
||||
Row,
|
||||
Col,
|
||||
CommercialDocHeader,
|
||||
} from 'components';
|
||||
import { useCashflowTransactionDrawerContext } from './CashflowTransactionDrawerProvider';
|
||||
|
||||
@@ -12,52 +15,45 @@ import { useCashflowTransactionDrawerContext } from './CashflowTransactionDrawer
|
||||
* Cashlflow transaction drawer detail Header.
|
||||
*/
|
||||
export default function CashflowTransactionDrawerHeader() {
|
||||
const {
|
||||
cashflowTransaction: {
|
||||
formatted_amount,
|
||||
transaction_type,
|
||||
transaction_number,
|
||||
reference_no,
|
||||
date,
|
||||
description,
|
||||
},
|
||||
} = useCashflowTransactionDrawerContext();
|
||||
const { cashflowTransaction } = useCashflowTransactionDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={'cashflow-drawer__content-header'}>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'total'} label={<T id={'total'} />}>
|
||||
<h3 class="amount">{formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocHeader>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'total'} label={<T id={'total'} />}>
|
||||
<h3 class="big-number">{cashflowTransaction.formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocHeader>
|
||||
|
||||
<DetailItem
|
||||
name={'transaction_type'}
|
||||
label={<T id={'cash_flow_drawer.label_transaction_type'} />}
|
||||
>
|
||||
{transaction_type}
|
||||
</DetailItem>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem
|
||||
name={'transaction_type'}
|
||||
label={<T id={'cash_flow_drawer.label_transaction_type'} />}
|
||||
>
|
||||
{cashflowTransaction.transaction_type_formatted}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
name={'transaction_number'}
|
||||
label={<T id={'cash_flow.drawer.label_transaction_no'} />}
|
||||
>
|
||||
{transaction_number}
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={<T id={'date'} />}
|
||||
children={<FormatDate value={date} />}
|
||||
/>
|
||||
<DetailItem name={'reference-no'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(reference_no, '-')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
<DetailItem
|
||||
name={'transaction_number'}
|
||||
label={<T id={'cash_flow.drawer.label_transaction_no'} />}
|
||||
>
|
||||
{cashflowTransaction.transaction_number}
|
||||
</DetailItem>
|
||||
|
||||
<div class="cashflow-drawer__content-description">
|
||||
<b class="title">
|
||||
<T id={'description'} />
|
||||
</b>
|
||||
: {defaultTo(description, '—')}
|
||||
</div>
|
||||
</div>
|
||||
<DetailItem label={<T id={'date'} />}>
|
||||
<FormatDate value={cashflowTransaction.date} />
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'reference-no'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(cashflowTransaction.reference_no, '-')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { DataTable, If, FormattedMessage as T } from 'components';
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useCashflowTransactionColumns } from './utils';
|
||||
import { useCashflowTransactionDrawerContext } from './CashflowTransactionDrawerProvider';
|
||||
@@ -14,9 +14,5 @@ export default function CashflowTransactionDrawerTable() {
|
||||
cashflowTransaction: { transactions },
|
||||
} = useCashflowTransactionDrawerContext();
|
||||
|
||||
return (
|
||||
<div className="cashflow-drawer__content-table">
|
||||
<DataTable columns={columns} data={transactions} />
|
||||
</div>
|
||||
);
|
||||
return <CommercialDocEntriesTable columns={columns} data={transactions} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { useAbilityContext } from 'hooks/utils';
|
||||
import { DrawerMainTabs } from 'components';
|
||||
import CreditNoteDetailActionsBar from './CreditNoteDetailActionsBar';
|
||||
import CreditNoteDetailPanel from './CreditNoteDetailPanel';
|
||||
import RefundCreditNoteTransactionsTable from './RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable';
|
||||
import ReconcileCreditNoteTransactionsTable from './ReconcileCreditNoteTransactions/ReconcileCreditNoteTransactionsTable';
|
||||
import { CreditNoteGLEntriesTable } from './JournalEntriesTransactions/JournalEntriesTransactionsTable';
|
||||
import {
|
||||
CreditNoteAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Credit Note view detail.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function CreditNoteDetail() {
|
||||
return (
|
||||
<CreditNoteRoot>
|
||||
<CreditNoteDetailActionsBar />
|
||||
<CreditNoteDetailsTabs />
|
||||
</CreditNoteRoot>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit note details tabs.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
function CreditNoteDetailsTabs() {
|
||||
const ability = useAbilityContext();
|
||||
|
||||
return (
|
||||
<DrawerMainTabs>
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<CreditNoteDetailPanel />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<CreditNoteGLEntriesTable />}
|
||||
/>
|
||||
|
||||
{ability.can(CreditNoteAction.View, AbilitySubject.CreditNote) && (
|
||||
<Tab
|
||||
title={intl.get('credit_note.drawer.label_refund_transactions')}
|
||||
id={'refund_transactions'}
|
||||
panel={<RefundCreditNoteTransactionsTable />}
|
||||
/>
|
||||
)}
|
||||
{ability.can(CreditNoteAction.View, AbilitySubject.CreditNote) && (
|
||||
<Tab
|
||||
title={intl.get('credit_note.drawer.label_invoices_reconciled')}
|
||||
id={'reconcile_transactions'}
|
||||
panel={<ReconcileCreditNoteTransactionsTable />}
|
||||
/>
|
||||
)}
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
const CreditNoteRoot = styled.div``;
|
||||
@@ -0,0 +1,119 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import {
|
||||
DrawerActionsBar,
|
||||
Can,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
If,
|
||||
} from 'components';
|
||||
import {
|
||||
CreditNoteAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { CreditNoteMenuItem } from './utils';
|
||||
|
||||
/**
|
||||
* Credit note detail actions bar.
|
||||
*/
|
||||
function CreditNoteDetailActionsBar({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { creditNoteId, creditNote } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
// Handle edit credit note.
|
||||
const handleEditCreditNote = () => {
|
||||
history.push(`/credit-notes/${creditNoteId}/edit`);
|
||||
closeDrawer('credit-note-detail-drawer');
|
||||
};
|
||||
|
||||
const handleRefundCreditNote = () => {
|
||||
openDialog('refund-credit-note', { creditNoteId });
|
||||
};
|
||||
|
||||
// Handle delete credit note.
|
||||
const handleDeleteCreditNote = () => {
|
||||
openAlert('credit-note-delete', { creditNoteId });
|
||||
};
|
||||
|
||||
const handleReconcileCreditNote = () => {
|
||||
openDialog('reconcile-credit-note', { creditNoteId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={CreditNoteAction.Edit} a={AbilitySubject.CreditNote}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={<T id={'credit_note.action.edit_credit_note'} />}
|
||||
onClick={handleEditCreditNote}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</Can>
|
||||
<Can I={CreditNoteAction.Refund} a={AbilitySubject.CreditNote}>
|
||||
<If condition={!creditNote.is_closed && !creditNote.is_draft}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="arrow-upward" iconSize={18} />}
|
||||
text={<T id={'refund'} />}
|
||||
onClick={handleRefundCreditNote}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</If>
|
||||
</Can>
|
||||
<Can I={CreditNoteAction.Delete} a={AbilitySubject.CreditNote}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleDeleteCreditNote}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={CreditNoteAction.Edit} a={AbilitySubject.CreditNote}>
|
||||
<If condition={creditNote.is_published && !creditNote.is_closed}>
|
||||
<NavbarDivider />
|
||||
<CreditNoteMenuItem
|
||||
payload={{
|
||||
onReconcile: handleReconcileCreditNote,
|
||||
}}
|
||||
/>
|
||||
</If>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(CreditNoteDetailActionsBar);
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { DrawerBody } from 'components';
|
||||
|
||||
import CreditNoteDetail from './CreditNoteDetail';
|
||||
import { CreditNoteDetailDrawerProvider } from './CreditNoteDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Credit note detail drawer content.
|
||||
*/
|
||||
export default function CreditNoteDetailDrawerContent({
|
||||
// #ownProp
|
||||
creditNoteId,
|
||||
}) {
|
||||
return (
|
||||
<CreditNoteDetailDrawerProvider creditNoteId={creditNoteId}>
|
||||
<DrawerBody>
|
||||
<CreditNoteDetail />
|
||||
</DrawerBody>
|
||||
</CreditNoteDetailDrawerProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
useCreditNote,
|
||||
useRefundCreditNote,
|
||||
useReconcileCreditNote,
|
||||
useReconcileCreditNotes,
|
||||
} from 'hooks/query';
|
||||
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
||||
|
||||
const CreditNoteDetailDrawerContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Credit note detail drawer provider.
|
||||
*/
|
||||
function CreditNoteDetailDrawerProvider({ creditNoteId, ...props }) {
|
||||
// Handle fetch vendor credit details.
|
||||
const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
|
||||
creditNoteId,
|
||||
{
|
||||
enabled: !!creditNoteId,
|
||||
},
|
||||
);
|
||||
|
||||
// Handle fetch refund credit note.
|
||||
const {
|
||||
data: refundCreditNote,
|
||||
isFetching: isRefundCreditNoteFetching,
|
||||
isLoading: isRefundCreditNoteLoading,
|
||||
} = useRefundCreditNote(creditNoteId, {
|
||||
enabled: !!creditNoteId,
|
||||
});
|
||||
|
||||
// Handle fetch refund credit note.
|
||||
const {
|
||||
data: reconcileCreditNotes,
|
||||
isLoading: isReconcileCreditNoteLoading,
|
||||
} = useReconcileCreditNotes(creditNoteId, {
|
||||
enabled: !!creditNoteId,
|
||||
});
|
||||
|
||||
// Handle fetch reconcile credit note details.
|
||||
const { isLoading: isReconcileCreditLoading, data: reconcileCreditNote } =
|
||||
useReconcileCreditNote(creditNoteId, {
|
||||
enabled: !!creditNoteId,
|
||||
});
|
||||
|
||||
const provider = {
|
||||
creditNote,
|
||||
refundCreditNote,
|
||||
reconcileCreditNote,
|
||||
reconcileCreditNotes,
|
||||
|
||||
isRefundCreditNoteLoading,
|
||||
isRefundCreditNoteFetching,
|
||||
creditNoteId,
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerLoading
|
||||
loading={
|
||||
isCreditNoteLoading ||
|
||||
isRefundCreditNoteLoading ||
|
||||
isReconcileCreditNoteLoading ||
|
||||
isReconcileCreditLoading
|
||||
}
|
||||
>
|
||||
<DrawerHeaderContent
|
||||
name="credit-note-detail-drawer"
|
||||
title={intl.get('credit_note.drawer.title', {
|
||||
number: creditNote.credit_note_number,
|
||||
})}
|
||||
/>
|
||||
<CreditNoteDetailDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
);
|
||||
}
|
||||
|
||||
const useCreditNoteDetailDrawerContext = () =>
|
||||
React.useContext(CreditNoteDetailDrawerContext);
|
||||
|
||||
export { CreditNoteDetailDrawerProvider, useCreditNoteDetailDrawerContext };
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
CommercialDocFooter,
|
||||
T,
|
||||
If,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
} from 'components';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Credit note detail footer
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function CreditNoteDetailFooter() {
|
||||
const { creditNote } = useCreditNoteDetailDrawerContext();
|
||||
return (
|
||||
<CommercialDocFooter>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<If condition={creditNote.terms_conditions}>
|
||||
<DetailItem label={<T id={'terms_conditions'} />}>
|
||||
{creditNote.terms_conditions}
|
||||
</DetailItem>
|
||||
</If>
|
||||
</DetailsMenu>
|
||||
</CommercialDocFooter>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { defaultTo } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
FormatDate,
|
||||
T,
|
||||
Row,
|
||||
Col,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
ButtonLink,
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
CustomerDrawerLink,
|
||||
} from 'components';
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
|
||||
import { CreditNoteDetailsStatus } from './utils';
|
||||
|
||||
/**
|
||||
* Credit note details drawer header.
|
||||
*/
|
||||
export default function CreditNoteDetailHeader() {
|
||||
const { creditNote } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<AmountItem label={intl.get('amount')}>
|
||||
<span class="big-number">{creditNote.formatted_amount}</span>
|
||||
</AmountItem>
|
||||
|
||||
<StatusItem>
|
||||
<CreditNoteDetailsStatus creditNote={creditNote} />
|
||||
</StatusItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem
|
||||
label={intl.get('credit_note.drawer.label_credit_note_no')}
|
||||
>
|
||||
{defaultTo(creditNote.credit_note_number, '-')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('customer_name')}>
|
||||
<CustomerDrawerLink customerId={creditNote.customer_id}>
|
||||
{creditNote.customer?.display_name}
|
||||
</CustomerDrawerLink>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
label={intl.get('credit_note.drawer.label_credit_note_date')}
|
||||
>
|
||||
<FormatDate value={creditNote.formatted_credit_note_date} />
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
textAlign={'right'}
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'180px'}
|
||||
>
|
||||
<DetailItem
|
||||
label={intl.get('credit_note.drawer.label_credits_remaining')}
|
||||
>
|
||||
<strong>{creditNote.formatted_credits_remaining}</strong>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(creditNote.reference_no, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('note')}
|
||||
children={defaultTo(creditNote.note, '-')}
|
||||
/>
|
||||
|
||||
<DetailItem
|
||||
label={<T id={'credit_note.drawer.label_created_at'} />}
|
||||
children={<FormatDate value={creditNote.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
`;
|
||||
|
||||
const AmountItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
`;
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
import { CommercialDocBox } from 'components';
|
||||
|
||||
import CreditNoteDetailHeader from './CreditNoteDetailHeader';
|
||||
import CreditNoteDetailTable from './CreditNoteDetailTable';
|
||||
import CreditNoteDetailTableFooter from './CreditNoteDetailTableFooter';
|
||||
import CreditNoteDetailFooter from './CreditNoteDetailFooter';
|
||||
|
||||
/**
|
||||
* Credit note details panel.
|
||||
*/
|
||||
export default function CreditNoteDetailPanel() {
|
||||
return (
|
||||
<CommercialDocBox>
|
||||
<CreditNoteDetailHeader />
|
||||
<CreditNoteDetailTable />
|
||||
<CreditNoteDetailTableFooter />
|
||||
<CreditNoteDetailFooter />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
import { useCreditNoteReadOnlyEntriesColumns } from './utils';
|
||||
|
||||
/**
|
||||
* Credit note detail table.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function CreditNoteDetailTable() {
|
||||
const {
|
||||
creditNote: { entries },
|
||||
} = useCreditNoteDetailDrawerContext();
|
||||
|
||||
// Credit note entries table columns.
|
||||
const columns = useCreditNoteReadOnlyEntriesColumns();
|
||||
|
||||
return (
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
FormatNumber,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from 'components';
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Credit note details panel footer.
|
||||
*/
|
||||
export default function CreditNoteDetailTableFooter() {
|
||||
const { creditNote } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<CreditNoteDetailsFooterRoot>
|
||||
<CreditNoteTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'credit_note.drawer.label_subtotal'} />}
|
||||
value={<FormatNumber value={creditNote.formatted_amount} />}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'credit_note.drawer.label_total'} />}
|
||||
value={creditNote.formatted_amount}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</CreditNoteTotalLines>
|
||||
</CreditNoteDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const CreditNoteDetailsFooterRoot = styled.div``;
|
||||
|
||||
export const CreditNoteTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'components';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from '../CreditNoteDetailDrawerProvider';
|
||||
import { useTransactionsByReference } from 'hooks/query';
|
||||
import { useJournalEntriesTransactionsColumns } from './components';
|
||||
|
||||
import JournalEntriesTable, {
|
||||
AmountDisplayedBaseCurrencyMessage,
|
||||
} from '../../../JournalEntriesTable/JournalEntriesTable';
|
||||
|
||||
/**
|
||||
* Journal entries table.
|
||||
*/
|
||||
export function CreditNoteGLEntriesTable() {
|
||||
const { creditNoteId } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
// Credit note GL entries table columns.
|
||||
const columns = useJournalEntriesTransactionsColumns();
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: creditNoteId,
|
||||
reference_type: 'creditNote',
|
||||
},
|
||||
{ enabled: !!creditNoteId },
|
||||
);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<AmountDisplayedBaseCurrencyMessage />
|
||||
|
||||
<JournalEntriesTable
|
||||
columns={columns}
|
||||
data={transactions}
|
||||
loading={isTransactionLoading}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormatDateCell, Icon } from '../../../../components';
|
||||
|
||||
import 'style/pages/JournalEntries/List.scss';
|
||||
|
||||
/**
|
||||
* Retrieve journal entries transactions table columns.
|
||||
*/
|
||||
export const useJournalEntriesTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('date'),
|
||||
accessor: 'date',
|
||||
accessor: 'formatted_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 140,
|
||||
className: 'date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('account_name'),
|
||||
accessor: 'account_name',
|
||||
width: 140,
|
||||
className: 'account_name',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('contact'),
|
||||
accessor: 'contactTypeFormatted',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
Header: intl.get('credit'),
|
||||
accessor: ({ credit }) => credit.formatted_amount,
|
||||
width: 100,
|
||||
className: 'credit',
|
||||
},
|
||||
{
|
||||
Header: intl.get('debit'),
|
||||
accessor: ({ debit }) => debit.formatted_amount,
|
||||
width: 100,
|
||||
className: 'debit',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { DataTable, Card } from 'components';
|
||||
|
||||
import { TableStyle } from 'common';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from '../CreditNoteDetailDrawerProvider';
|
||||
import {
|
||||
useReconcileCreditTransactionsTableColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Reconcile credit transactions table.
|
||||
*/
|
||||
function RefundCreditNoteTransactionsTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
}) {
|
||||
// Credit note drawer context.
|
||||
const { reconcileCreditNotes } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
// Reconcile credit transactions table columns.
|
||||
const columns = useReconcileCreditTransactionsTableColumns();
|
||||
|
||||
// Handle delete reconile credit.
|
||||
const handleDeleteReconcileCreditNote = ({ id }) => {
|
||||
openAlert('reconcile-credit-delete', { creditNoteId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={reconcileCreditNotes}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteReconcileCreditNote,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
className={'datatable--refund-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Can, FormatDateCell, Icon } from 'components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
CreditNoteAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({ payload: { onDelete }, row: { original } }) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={CreditNoteAction.Delete} a={AbilitySubject.CreditNote}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit note reconcilation with invoices table columns.
|
||||
*/
|
||||
export function useReconcileCreditTransactionsTableColumns() {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_credit_note_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 100,
|
||||
className: 'date',
|
||||
},
|
||||
{
|
||||
Header: intl.get('invoice_no'),
|
||||
accessor: 'invoice_number',
|
||||
width: 100,
|
||||
className: 'invoice_number',
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formtted_amount',
|
||||
width: 100,
|
||||
className: 'amount',
|
||||
align: 'right',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import { DataTable, Card } from 'components';
|
||||
|
||||
import { TableStyle } from 'common';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { useCreditNoteDetailDrawerContext } from '../CreditNoteDetailDrawerProvider';
|
||||
import {
|
||||
useRefundCreditTransactionsTableColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Refund credit note transactions table.
|
||||
*/
|
||||
function RefundCreditNoteTransactionsTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
}) {
|
||||
const { refundCreditNote } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
// Refund credit transactions table columns.
|
||||
const columns = useRefundCreditTransactionsTableColumns();
|
||||
|
||||
// Handle delete refund credit.
|
||||
const handleDeleteRefundCreditNote = ({ id }) => {
|
||||
openAlert('refund-credit-delete', { creditNoteId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={refundCreditNote}
|
||||
ContextMenu={ActionsMenu}
|
||||
styleName={TableStyle.Constrant}
|
||||
payload={{
|
||||
onDelete: handleDeleteRefundCreditNote,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Can, FormatDateCell, Icon } from 'components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
CreditNoteAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({ payload: { onDelete }, row: { original } }) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={CreditNoteAction.Delete} a={AbilitySubject.CreditNote}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export function useRefundCreditTransactionsTableColumns() {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 100,
|
||||
className: 'date',
|
||||
},
|
||||
{
|
||||
Header: intl.get('refund_credit_transactions.column.amount_refunded'),
|
||||
accessor: 'formtted_amount',
|
||||
width: 100,
|
||||
className: 'amount',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
id: 'from_account',
|
||||
Header: intl.get(
|
||||
'refund_credit_transactions.column.withdrawal_account',
|
||||
),
|
||||
accessor: ({ from_account }) => from_account.name,
|
||||
width: 100,
|
||||
className: 'from_account',
|
||||
},
|
||||
{
|
||||
id: 'reference_no',
|
||||
Header: intl.get('reference_no'),
|
||||
accessor: 'reference_no',
|
||||
width: 100,
|
||||
className: 'reference_no',
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
33
src/containers/Drawers/CreditNoteDetailDrawer/index.js
Normal file
33
src/containers/Drawers/CreditNoteDetailDrawer/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { Drawer, DrawerSuspense } from 'components';
|
||||
import withDrawers from 'containers/Drawer/withDrawers';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const CreditNoteDetailDrawerContent = React.lazy(() =>
|
||||
import('./CreditNoteDetailDrawerContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Credit note detail drawer.
|
||||
*/
|
||||
function CreditNoteDetailDrawer({
|
||||
name,
|
||||
// #withDrawer
|
||||
isOpen,
|
||||
payload: { creditNoteId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
<CreditNoteDetailDrawerContent creditNoteId={creditNoteId} />
|
||||
</DrawerSuspense>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
export default compose(withDrawers())(CreditNoteDetailDrawer);
|
||||
117
src/containers/Drawers/CreditNoteDetailDrawer/utils.js
Normal file
117
src/containers/Drawers/CreditNoteDetailDrawer/utils.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Tag,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import {
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
FormatNumberCell,
|
||||
Choose,
|
||||
} from '../../../components';
|
||||
|
||||
export const useCreditNoteReadOnlyEntriesColumns = () =>
|
||||
React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('product_and_service'),
|
||||
accessor: 'item.name',
|
||||
width: 150,
|
||||
className: 'name',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('description'),
|
||||
accessor: 'description',
|
||||
className: 'description',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('quantity'),
|
||||
accessor: 'quantity',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'rate',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
Cell: FormatNumberCell,
|
||||
width: 100,
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Credit note more actions mneu.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function CreditNoteMenuItem({ payload: { onReconcile } }) {
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={onReconcile}
|
||||
text={<T id={'credit_note.action.reconcile_with_invoices'} />}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button icon={<Icon icon="more-vert" iconSize={16} />} minimal={true} />
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit note details status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function CreditNoteDetailsStatus({ creditNote }) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={creditNote.is_open}>
|
||||
<Tag intent={Intent.WARNING} round={true}>
|
||||
<T id={'open'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_closed}>
|
||||
<Tag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'closed'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_draft}>
|
||||
<Tag intent={Intent.NONE} round={true} minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
@@ -28,15 +28,10 @@ export default function CustomerDetailsHeader() {
|
||||
<h3 class="big-number">{customer.formatted_balance}</h3>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
label={<T id={'customer.drawer.label.customer_name'} />}
|
||||
name={'name'}
|
||||
children={customer?.display_name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={<T id={'customer.drawer.label.customer_type'} />}
|
||||
name={'type'}
|
||||
children={customer?.customer_type}
|
||||
children={customer?.formatted_customer_type }
|
||||
/>
|
||||
<DetailItem label={<T id={'customer.drawer.label.unused_credits'} />}>
|
||||
0
|
||||
@@ -44,6 +39,13 @@ export default function CustomerDetailsHeader() {
|
||||
</DetailsMenu>
|
||||
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'175px'}>
|
||||
<DetailItem
|
||||
label={<T id={'customer.drawer.label.customer_name'} />}
|
||||
name={'name'}
|
||||
>
|
||||
<strong>{customer?.display_name}</strong>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
label={<T id={'customer.drawer.label.company_name'} />}
|
||||
children={defaultTo(customer?.company_name, '--')}
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DrawerMainTabs } from 'components';
|
||||
|
||||
import EstimateDetailActionsBar from './EstimateDetailActionsBar';
|
||||
import EstimateDetailPanel from './EstimateDetailPanel';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import EstimateDetailsCls from 'style/components/Drawers/EstimateDetails.module.scss';
|
||||
/**
|
||||
* Estimate details tabs.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
function EstimateDetailsTabs() {
|
||||
return (
|
||||
<DrawerMainTabs>
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<EstimateDetailPanel />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate view detail
|
||||
*/
|
||||
export default function EstimateDetail() {
|
||||
return (
|
||||
<div className={clsx(EstimateDetailsCls.root)}>
|
||||
<DrawerMainTabs>
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<EstimateDetailPanel />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
</div>
|
||||
<EstimateDetailsRoot>
|
||||
<EstimateDetailActionsBar />
|
||||
<EstimateDetailsTabs />
|
||||
</EstimateDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const EstimateDetailsRoot = styled.div``;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
@@ -19,7 +19,13 @@ import {
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
|
||||
import { Icon, FormattedMessage as T, MoreMenuItems, Can } from 'components';
|
||||
import {
|
||||
DrawerActionsBar,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
MoreMenuItems,
|
||||
Can,
|
||||
} from 'components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -36,8 +42,10 @@ function EstimateDetailActionsBar({
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
// Estimate details drawer context.
|
||||
const { estimateId } = useEstimateDetailDrawerContext();
|
||||
|
||||
// History.
|
||||
const history = useHistory();
|
||||
|
||||
// Handle edit sale estimate.
|
||||
@@ -61,7 +69,7 @@ function EstimateDetailActionsBar({
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={SaleEstimateAction.Edit} a={AbilitySubject.Estimate}>
|
||||
<Button
|
||||
@@ -90,13 +98,15 @@ function EstimateDetailActionsBar({
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</Can>
|
||||
<MoreMenuItems
|
||||
payload={{
|
||||
onNotifyViaSMS: handleNotifyViaSMS,
|
||||
}}
|
||||
/>
|
||||
<Can I={SaleEstimateAction.NotifyBySms} a={AbilitySubject.Estimate}>
|
||||
<MoreMenuItems
|
||||
payload={{
|
||||
onNotifyViaSMS: handleNotifyViaSMS,
|
||||
}}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ function EstimateDetailDrawerProvider({ estimateId, ...props }) {
|
||||
<DrawerLoading loading={isEstimateLoading}>
|
||||
<DrawerHeaderContent
|
||||
name="estimate-detail-drawer"
|
||||
title={intl.get('estimate_details')}
|
||||
title={intl.get('estimate.drawer.title', {
|
||||
number: estimate.estimate_number,
|
||||
})}
|
||||
/>
|
||||
<EstimateDetailDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
|
||||
@@ -1,40 +1,35 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { T, TotalLines, TotalLine, If } from 'components';
|
||||
import {
|
||||
CommercialDocFooter,
|
||||
T,
|
||||
If,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
} from 'components';
|
||||
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
|
||||
import { FormatNumber } from '../../../components';
|
||||
|
||||
import EstimateDetailsCls from 'style/components/Drawers/EstimateDetails.module.scss';
|
||||
|
||||
/**
|
||||
* Estimate details panel footer content.
|
||||
* Estimate details footer.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function EstimateDetailFooter() {
|
||||
const { estimate } = useEstimateDetailDrawerContext();
|
||||
|
||||
|
||||
return (
|
||||
<div className={clsx(EstimateDetailsCls.detail_panel_footer)}>
|
||||
<TotalLines className={clsx(EstimateDetailsCls.total_lines)}>
|
||||
<TotalLine
|
||||
title={<T id={'estimate.details.subtotal'} />}
|
||||
value={<FormatNumber value={estimate.amount} />}
|
||||
className={EstimateDetailsCls.total_line_subtotal}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'estimate.details.total'} />}
|
||||
value={estimate.formatted_amount}
|
||||
className={EstimateDetailsCls.total_line_total}
|
||||
/>
|
||||
</TotalLines>
|
||||
|
||||
<If condition={false}>
|
||||
<div className={clsx(EstimateDetailsCls.detail_panel_note)}>
|
||||
<p>
|
||||
<b><T id={'estimate.details.note'} />:</b> --
|
||||
</p>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<CommercialDocFooter>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<If condition={estimate.terms_conditions}>
|
||||
<DetailItem label={<T id={'estimate.details.terms_conditions'} />}>
|
||||
{estimate.terms_conditions}
|
||||
</DetailItem>
|
||||
</If>
|
||||
<If condition={estimate.note}>
|
||||
<DetailItem label={<T id={'estimate.details.note'} />}>
|
||||
{estimate.note}
|
||||
</DetailItem>
|
||||
</If>
|
||||
</DetailsMenu>
|
||||
</CommercialDocFooter>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { defaultTo } from 'lodash';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { FormatDate, T, DetailsMenu, DetailItem } from 'components';
|
||||
import {
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
FormatDate,
|
||||
T,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
Row,
|
||||
Col,
|
||||
ButtonLink,
|
||||
CustomerDrawerLink,
|
||||
} from 'components';
|
||||
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
|
||||
|
||||
import EstimateDetailsCls from 'style/components/Drawers/EstimateDetails.module.scss';
|
||||
import { EstimateDetailsStatus } from './components';
|
||||
|
||||
/**
|
||||
* Estimate read-only details drawer header.
|
||||
@@ -15,39 +25,70 @@ export default function EstimateDetailHeader() {
|
||||
const { estimate } = useEstimateDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(EstimateDetailsCls.detail_panel_header)}>
|
||||
<DetailsMenu>
|
||||
<DetailItem label={intl.get('amount')}>
|
||||
<span class="big-number">{estimate.formatted_amount}</span>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={intl.get('estimate.details.estimate_number')}
|
||||
children={defaultTo(estimate.estimate_number, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('customer_name')}
|
||||
children={estimate.customer?.display_name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('estimate_date')}
|
||||
children={estimate.formatted_estimate_date}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('expiration_date')}
|
||||
children={estimate.formatted_expiration_date}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<AmountEstimateDetail label={intl.get('amount')}>
|
||||
<span class="big-number">{estimate.formatted_amount}</span>
|
||||
</AmountEstimateDetail>
|
||||
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'140px'}>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(estimate.reference, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={<T id={'estimate.details.created_at'} />}
|
||||
children={<FormatDate value={estimate.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
<EstimateStatusDetail>
|
||||
<EstimateDetailsStatus estimate={estimate} />
|
||||
</EstimateStatusDetail>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem
|
||||
label={intl.get('estimate.details.estimate_number')}
|
||||
children={defaultTo(estimate.estimate_number, '-')}
|
||||
/>
|
||||
|
||||
<DetailItem label={intl.get('customer_name')}>
|
||||
<CustomerDrawerLink customerId={estimate.customer_id}>
|
||||
{estimate.customer?.display_name}
|
||||
</CustomerDrawerLink>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
label={intl.get('estimate_date')}
|
||||
children={estimate.formatted_estimate_date}
|
||||
/>
|
||||
|
||||
<DetailItem
|
||||
label={intl.get('expiration_date')}
|
||||
children={estimate.formatted_expiration_date}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
textAlign={'right'}
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'180px'}
|
||||
>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(estimate.reference, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={<T id={'estimate.details.created_at'} />}
|
||||
children={<FormatDate value={estimate.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const EstimateStatusDetail = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
`;
|
||||
const AmountEstimateDetail = styled(DetailItem)`
|
||||
width: 50%;
|
||||
`;
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { CommercialDocBox } from 'components';
|
||||
|
||||
import EstimateDetailActionsBar from './EstimateDetailActionsBar';
|
||||
import EstimateDetailHeader from './EstimateDetailHeader';
|
||||
import EstimateDetailTable from './EstimateDetailTable';
|
||||
|
||||
import EstimateDetailsCls from 'style/components/Drawers/EstimateDetails.module.scss';
|
||||
import EstimateDetailTableFooter from './EstimateDetailTableFooter';
|
||||
import EstimateDetailFooter from './EstimateDetailFooter';
|
||||
|
||||
|
||||
export default function EstimateDetailTab() {
|
||||
return (
|
||||
<div className={clsx(EstimateDetailsCls.detail_panel)}>
|
||||
<EstimateDetailActionsBar />
|
||||
|
||||
<Card>
|
||||
<EstimateDetailHeader />
|
||||
<EstimateDetailTable />
|
||||
<EstimateDetailFooter />
|
||||
</Card>
|
||||
</div>
|
||||
<CommercialDocBox>
|
||||
<EstimateDetailHeader />
|
||||
<EstimateDetailTable />
|
||||
<EstimateDetailTableFooter />
|
||||
<EstimateDetailFooter />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { DataTable } from 'components';
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
|
||||
|
||||
import { useEstimateReadonlyEntriesColumns } from './utils';
|
||||
|
||||
import EstimateDetailsCls from 'style/components/Drawers/EstimateDetails.module.scss';
|
||||
import { TableStyle } from '../../../common';
|
||||
|
||||
/**
|
||||
* Estimate detail table.
|
||||
@@ -20,12 +19,10 @@ export default function EstimateDetailTable() {
|
||||
const columns = useEstimateReadonlyEntriesColumns();
|
||||
|
||||
return (
|
||||
<div className={clsx(EstimateDetailsCls.detail_panel_table)}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
</div>
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
FormatNumber,
|
||||
} from 'components';
|
||||
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Estimate details panel footer content.
|
||||
*/
|
||||
export default function EstimateDetailTableFooter() {
|
||||
const { estimate } = useEstimateDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<EstimateDetailsFooterRoot>
|
||||
<EstimateTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'estimate.details.subtotal'} />}
|
||||
value={<FormatNumber value={estimate.amount} />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'estimate.details.total'} />}
|
||||
value={estimate.formatted_amount}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</EstimateTotalLines>
|
||||
</EstimateDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const EstimateDetailsFooterRoot = styled.div``;
|
||||
|
||||
export const EstimateTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
40
src/containers/Drawers/EstimateDetailDrawer/components.js
Normal file
40
src/containers/Drawers/EstimateDetailDrawer/components.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
|
||||
import { T, Choose } from 'components';
|
||||
|
||||
/**
|
||||
* Estimate details status.
|
||||
* @return {React.JSX}
|
||||
*/
|
||||
export function EstimateDetailsStatus({ estimate }) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={estimate.is_approved}>
|
||||
<Tag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'approved'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
<Choose.When condition={estimate.is_rejected}>
|
||||
<Tag intent={Intent.DANGER} round={true}>
|
||||
<T id={'rejected'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
<Choose.When condition={estimate.is_expired}>
|
||||
<Tag intent={Intent.WARNING} round={true}>
|
||||
<T id={'estimate.status.expired'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
<Choose.When condition={estimate.is_delivered}>
|
||||
<Tag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'delivered'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<Tag round={true} minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
@@ -8,15 +8,17 @@ import {
|
||||
Intent,
|
||||
NavbarDivider,
|
||||
} from '@blueprintjs/core';
|
||||
import { Can, FormattedMessage as T } from 'components';
|
||||
import { DrawerActionsBar, Can, FormattedMessage as T } from 'components';
|
||||
|
||||
import { ExpenseAction, AbilitySubject } from '../../../common/abilityOption';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Expense drawer action bar.
|
||||
*/
|
||||
@@ -28,6 +30,8 @@ function ExpenseDrawerActionBar({
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// Expense drawer context.
|
||||
const { expense } = useExpenseDrawerContext();
|
||||
|
||||
// Handle the expense edit action.
|
||||
@@ -42,7 +46,7 @@ function ExpenseDrawerActionBar({
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={ExpenseAction.Edit} a={AbilitySubject.Expense}>
|
||||
<Button
|
||||
@@ -63,7 +67,7 @@ function ExpenseDrawerActionBar({
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
|
||||
import { DrawerBody } from 'components';
|
||||
|
||||
import 'style/components/Drawers/ExpenseDrawer.scss';
|
||||
|
||||
import { ExpenseDrawerProvider } from './ExpenseDrawerProvider';
|
||||
import ExpenseDrawerDetails from './ExpenseDrawerDetails';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { CommercialDocBox } from '../../../components';
|
||||
|
||||
import ExpenseDrawerActionBar from './ExpenseDrawerActionBar';
|
||||
import ExpenseDrawerHeader from './ExpenseDrawerHeader';
|
||||
@@ -12,16 +13,16 @@ import ExpenseDrawerFooter from './ExpenseDrawerFooter';
|
||||
*/
|
||||
export default function ExpenseDrawerDetails() {
|
||||
return (
|
||||
<div className={'expense-drawer'}>
|
||||
<ExpenseDetailsRoot>
|
||||
<ExpenseDrawerActionBar />
|
||||
|
||||
<div className="expense-drawer__content">
|
||||
<Card>
|
||||
<ExpenseDrawerHeader />
|
||||
<ExpenseDrawerTable />
|
||||
<ExpenseDrawerFooter />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<CommercialDocBox>
|
||||
<ExpenseDrawerHeader />
|
||||
<ExpenseDrawerTable />
|
||||
<ExpenseDrawerFooter />
|
||||
</CommercialDocBox>
|
||||
</ExpenseDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const ExpenseDetailsRoot = styled.div``;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import React from 'react';
|
||||
import { T } from 'components';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { FormatNumber } from '../../../components';
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from 'components';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
import { FormatNumber, TotalLine } from '../../../components';
|
||||
|
||||
/**
|
||||
* Footer details of expense readonly details.
|
||||
@@ -11,23 +17,26 @@ export default function ExpenseDrawerFooter() {
|
||||
const { expense } = useExpenseDrawerContext();
|
||||
|
||||
return (
|
||||
<div className="expense-drawer__content-footer">
|
||||
<div class="total-lines">
|
||||
<div class="total-lines__line total-lines__line--subtotal">
|
||||
<div class="title">
|
||||
<T id={'expense.details.subtotal'} />
|
||||
</div>
|
||||
<div class="amount">
|
||||
{<FormatNumber value={expense.total_amount} />}
|
||||
</div>
|
||||
</div>
|
||||
<div class="total-lines__line total-lines__line--total">
|
||||
<div class="title">
|
||||
<T id={'expense.details.total'} />
|
||||
</div>
|
||||
<div class="amount">{expense.formatted_amount}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExpenseDetailsFooterRoot>
|
||||
<ExpenseTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'expense.details.subtotal'} />}
|
||||
value={<FormatNumber value={expense.total_amount} />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'expense.details.total'} />}
|
||||
value={<FormatNumber value={expense.formatted_amount} />}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</ExpenseTotalLines>
|
||||
</ExpenseDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const ExpenseDetailsFooterRoot = styled.div``;
|
||||
|
||||
export const ExpenseTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
@@ -1,58 +1,79 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { defaultTo } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DetailItem, DetailsMenu, Money } from 'components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import {
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
Row,
|
||||
Col,
|
||||
DetailItem,
|
||||
DetailsMenu,
|
||||
Money,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
import { ExpenseDetailsStatus } from './components';
|
||||
|
||||
/**
|
||||
* Expense drawer content.
|
||||
*/
|
||||
export default function ExpenseDrawerHeader() {
|
||||
const {
|
||||
expense: {
|
||||
total_amount,
|
||||
payment_date,
|
||||
currency_code,
|
||||
reference_no,
|
||||
description,
|
||||
published_at,
|
||||
},
|
||||
} = useExpenseDrawerContext();
|
||||
const { expense } = useExpenseDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={'expense-drawer__content-header'}>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'amount'} label={<T id={'full_amount'} />}>
|
||||
<h3 class="big-number">
|
||||
<Money amount={total_amount} currency={currency_code} />
|
||||
</h3>
|
||||
</DetailItem>
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'amount'} label={<T id={'full_amount'} />}>
|
||||
<h3 class="big-number">{expense.formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'date'} label={<T id={'date'} />}>
|
||||
{moment(payment_date).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
<StatusDetailItem>
|
||||
<ExpenseDetailsStatus expense={expense} />
|
||||
</StatusDetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
|
||||
<DetailItem name={'currency'} label={<T id={'currency'} />}>
|
||||
{currency_code}
|
||||
</DetailItem>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem name={'date'} label={<T id={'date'} />}>
|
||||
{moment(expense.payment_date).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'reference'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(reference_no, '-')}
|
||||
</DetailItem>
|
||||
<DetailItem name={'reference'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(expense.reference_no, '-')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={<T id={'published_at'} />}>
|
||||
{moment(published_at).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
<DetailItem label={<T id={'description'} />}>
|
||||
{defaultTo(expense.description, '—')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
|
||||
<DetailsMenu direction={'horizantal'}>
|
||||
<DetailItem label={<T id={'description'} />}>
|
||||
{defaultTo(description, '—')}
|
||||
</DetailItem>
|
||||
<DetailItem label={<T id={'created_at'} />}>2021 Aug 24</DetailItem>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
textAlign={'right'}
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'180px'}
|
||||
>
|
||||
<DetailItem label={<T id={'published_at'} />}>
|
||||
{moment(expense.published_at).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={<T id={'created_at'} />}>2021 Aug 24</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
`;
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import React from 'react';
|
||||
import { DataTable } from 'components';
|
||||
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useExpenseReadEntriesColumns } from './utils';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
|
||||
import { TableStyle } from '../../../common';
|
||||
|
||||
/**
|
||||
* Expense details table.
|
||||
*/
|
||||
export default function ExpenseDrawerTable() {
|
||||
// Expense readonly entries columns.
|
||||
const columns = useExpenseReadEntriesColumns();
|
||||
|
||||
// Expense drawer context.
|
||||
const { expense } = useExpenseDrawerContext();
|
||||
|
||||
return (
|
||||
<div className="expense-drawer__content--table">
|
||||
<DataTable columns={columns} data={expense.categories} />
|
||||
</div>
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={expense.categories}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
20
src/containers/Drawers/ExpenseDrawer/components.js
Normal file
20
src/containers/Drawers/ExpenseDrawer/components.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Tag, Intent } from '@blueprintjs/core';
|
||||
|
||||
import { T } from 'components';
|
||||
|
||||
/**
|
||||
* Expense details status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function ExpenseDetailsStatus({ expense }) {
|
||||
return expense.is_published ? (
|
||||
<Tag round={true} minimal={true}>
|
||||
<T id={'published'} />
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag round={true} intent={Intent.WARNING}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ function ExpenseDrawer({
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
title={intl.get('expense')}
|
||||
title={intl.get('expense.drawer.title')}
|
||||
size={'65%'}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
>
|
||||
|
||||
@@ -1,26 +1,48 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DrawerMainTabs } from 'components';
|
||||
import InventoryAdjustmentDetailTab from './InventoryAdjustmentDetailTab';
|
||||
import InventoryAdjustmentDetailActionsBar from './InventoryAdjustmentDetailActionsBar';
|
||||
import InventoryAdjustmentDetailHeader from './InventoryAdjustmentDetailHeader';
|
||||
import InventoryAdjustmentDetailTable from './InventoryAdjustmentDetailTable';
|
||||
|
||||
import InventoryAdjustmentDrawerCls from 'style/components/Drawers/InventoryAdjustmentDrawer.module.scss';
|
||||
import InventoryAdjustmentDetailGLEntriesPanel from './InventoryAdjustmentDetailGLEntriesPanel';
|
||||
|
||||
/**
|
||||
* Inventory adjustment detail
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function InventoryAdjustmentDetail() {
|
||||
return (
|
||||
<div className={clsx(InventoryAdjustmentDrawerCls.detail_panel)}>
|
||||
<InventoryAdjustmentDetailsRoot>
|
||||
<InventoryAdjustmentDetailActionsBar />
|
||||
|
||||
<Card>
|
||||
<InventoryAdjustmentDetailHeader />
|
||||
<InventoryAdjustmentDetailTable />
|
||||
</Card>
|
||||
</div>
|
||||
<InventoryAdjustmentDetailTabs />
|
||||
</InventoryAdjustmentDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invenoty adjusment details tabs.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
function InventoryAdjustmentDetailTabs() {
|
||||
return (
|
||||
<DrawerMainTabs
|
||||
renderActiveTabPanelOnly={true}
|
||||
defaultSelectedTabId="details"
|
||||
>
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<InventoryAdjustmentDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<InventoryAdjustmentDetailGLEntriesPanel />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
const InventoryAdjustmentDetailsRoot = styled.div``;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Button, NavbarGroup, Classes, Intent } from '@blueprintjs/core';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { useInventoryAdjustmentDrawerContext } from './InventoryAdjustmentDrawerProvider';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { Icon, FormattedMessage as T, Can } from 'components';
|
||||
import { Icon, DrawerActionsBar, FormattedMessage as T, Can } from 'components';
|
||||
import {
|
||||
InventoryAdjustmentAction,
|
||||
AbilitySubject,
|
||||
@@ -34,7 +32,7 @@ function InventoryAdjustmentDetailActionsBar({
|
||||
I={InventoryAdjustmentAction.Delete}
|
||||
a={AbilitySubject.InventoryAdjustment}
|
||||
>
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
@@ -44,7 +42,7 @@ function InventoryAdjustmentDetailActionsBar({
|
||||
onClick={handleDeleteInventoryAdjustment}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
</Can>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { useTransactionsByReference } from 'hooks/query';
|
||||
import { useInventoryAdjustmentDrawerContext } from './InventoryAdjustmentDrawerProvider';
|
||||
|
||||
import JournalEntriesTable, {
|
||||
AmountDisplayedBaseCurrencyMessage,
|
||||
} from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
|
||||
/**
|
||||
* Inentory adjustmet detail GL entries panel.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function InventoryAdjustmentDetailGLEntriesPanel() {
|
||||
const { inventoryId } = useInventoryAdjustmentDrawerContext();
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: inventoryId,
|
||||
reference_type: 'inventoryAdjustment',
|
||||
},
|
||||
{ enabled: !!inventoryId },
|
||||
);
|
||||
|
||||
return (
|
||||
<InventoryAdjustmentGLEntriesRoot>
|
||||
<AmountDisplayedBaseCurrencyMessage />
|
||||
<JournalEntriesTable
|
||||
loading={isTransactionLoading}
|
||||
transactions={transactions}
|
||||
/>
|
||||
</InventoryAdjustmentGLEntriesRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const InventoryAdjustmentGLEntriesRoot = styled(Card)``;
|
||||
@@ -14,43 +14,37 @@ import InventoryAdjustmentDrawerCls from 'style/components/Drawers/InventoryAdju
|
||||
* Inventory detail header.
|
||||
*/
|
||||
export default function InventoryAdjustmentDetailHeader() {
|
||||
const {
|
||||
inventoryAdjustment: {
|
||||
date,
|
||||
type,
|
||||
adjustment_account,
|
||||
inventory_direction,
|
||||
description,
|
||||
reference_no,
|
||||
reason,
|
||||
published_at,
|
||||
created_at,
|
||||
},
|
||||
} = useInventoryAdjustmentDrawerContext();
|
||||
const { inventoryAdjustment } = useInventoryAdjustmentDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(InventoryAdjustmentDrawerCls.detail_panel_header)}>
|
||||
<DetailsMenu>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem label={intl.get('date')}>
|
||||
{moment(date).format('YYYY MMM DD')}
|
||||
{moment(inventoryAdjustment.date).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('type')}>{type}</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('type')}>
|
||||
{inventoryAdjustment.formatted_type}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('adjustment_account')}>
|
||||
{adjustment_account.name}
|
||||
{inventoryAdjustment.adjustment_account.name}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'reference'} label={intl.get('reference_no')}>
|
||||
{defaultTo(reference_no, '-')}
|
||||
{defaultTo(inventoryAdjustment.reference_no, '-')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('published_at')}>
|
||||
{moment(published_at).format('YYYY MMM DD')}
|
||||
{moment(inventoryAdjustment.published_at).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
<DetailsMenu direction={'horizantal'}>
|
||||
|
||||
<DetailItem label={intl.get('reason')}>
|
||||
{defaultTo(reason, '—')}
|
||||
{defaultTo(inventoryAdjustment.reason, '—')}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('created_at')}>
|
||||
{moment(created_at).format('YYYY MMM DD')}
|
||||
{moment(inventoryAdjustment.created_at).format('YYYY MMM DD')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CommercialDocBox } from 'components';
|
||||
|
||||
import InventoryAdjustmentDetailHeader from './InventoryAdjustmentDetailHeader';
|
||||
import InventoryAdjustmentDetailTable from './InventoryAdjustmentDetailTable';
|
||||
|
||||
export default function InventoryAdjustmentDetailTab() {
|
||||
return (
|
||||
<CommercialDocBox>
|
||||
<InventoryAdjustmentDetailHeader />
|
||||
<InventoryAdjustmentDetailTable />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +1,24 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { DataTable } from 'components';
|
||||
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
import { useInventoryAdjustmentEntriesColumns } from './utils';
|
||||
import { useInventoryAdjustmentDrawerContext } from './InventoryAdjustmentDrawerProvider';
|
||||
|
||||
import InventoryAdjustmentDrawerCls from 'style/components/Drawers/InventoryAdjustmentDrawer.module.scss';
|
||||
|
||||
/**
|
||||
* Inventory adjustment detail entries table.
|
||||
*/
|
||||
export default function InventoryAdjustmentDetailTable() {
|
||||
// Inventory adjustment entries columns.
|
||||
const columns = useInventoryAdjustmentEntriesColumns();
|
||||
|
||||
const {
|
||||
inventoryAdjustment: { entries },
|
||||
} = useInventoryAdjustmentDrawerContext();
|
||||
// Inventory adjustment details drawer context.
|
||||
const { inventoryAdjustment } = useInventoryAdjustmentDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(InventoryAdjustmentDrawerCls.detail_panel_table)}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
</div>
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={inventoryAdjustment.entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@ function InventoryAdjustmentDetailDrawer({
|
||||
payload: { inventoryId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer isOpen={isOpen} name={name} size={'750px'}>
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
<InventoryAdjustmentDrawerContent inventoryId={inventoryId} />
|
||||
</DrawerSuspense>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const useInventoryAdjustmentEntriesColumns = () =>
|
||||
React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('product_and_service'),
|
||||
Header: intl.get('inventory_adjustment.column.product'),
|
||||
accessor: 'item.name',
|
||||
width: 150,
|
||||
className: 'name',
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { useAbilityContext } from 'hooks/utils';
|
||||
import { DrawerMainTabs } from 'components';
|
||||
|
||||
import JournalEntriesTable from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
import {
|
||||
PaymentReceiveAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
import InvoiceDetailActionsBar from './InvoiceDetailActionsBar';
|
||||
import InvoiceGLEntriesTable from './InvoiceGLEntriesTable';
|
||||
import InvoicePaymentTransactionsTable from './InvoicePaymentTransactions/InvoicePaymentTransactionsTable';
|
||||
import InvoiceDetailTab from './InvoiceDetailTab';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss';
|
||||
/**
|
||||
* Invoice details tabs.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
function InvoiceDetailsTabs() {
|
||||
const ability = useAbilityContext();
|
||||
|
||||
return (
|
||||
<DrawerMainTabs
|
||||
renderActiveTabPanelOnly={true}
|
||||
defaultSelectedTabId="details"
|
||||
>
|
||||
<Tab
|
||||
title={intl.get('overview')}
|
||||
id={'details'}
|
||||
panel={<InvoiceDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<InvoiceGLEntriesTable />}
|
||||
/>
|
||||
{ability.can(
|
||||
PaymentReceiveAction.View,
|
||||
AbilitySubject.PaymentReceive,
|
||||
) && (
|
||||
<Tab
|
||||
title={intl.get('payment_transactions')}
|
||||
id={'payment_transactions'}
|
||||
panel={<InvoicePaymentTransactionsTable />}
|
||||
/>
|
||||
)}
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice view detail.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function InvoiceDetail() {
|
||||
const { transactions } = useInvoiceDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(InvoiceDrawerCls.invoice_details)}>
|
||||
<DrawerMainTabs defaultSelectedTabId="details">
|
||||
<Tab
|
||||
title={intl.get('details')}
|
||||
id={'details'}
|
||||
panel={<InvoiceDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<JournalEntriesTable transactions={transactions} />}
|
||||
/>
|
||||
{/* <Tab
|
||||
title={intl.get('payment_transactions')}
|
||||
id={'payment_transactions'}
|
||||
// panel={}
|
||||
/> */}
|
||||
</DrawerMainTabs>
|
||||
</div>
|
||||
<InvoiceDetailsRoot>
|
||||
<InvoiceDetailActionsBar />
|
||||
<InvoiceDetailsTabs />
|
||||
</InvoiceDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const InvoiceDetailsRoot = styled.div``;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
@@ -8,7 +7,6 @@ import {
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
@@ -16,7 +14,13 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { If, Can, Icon, FormattedMessage as T } from 'components';
|
||||
import {
|
||||
If,
|
||||
Can,
|
||||
Icon,
|
||||
DrawerActionsBar,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
import {
|
||||
SaleInvoiceAction,
|
||||
PaymentReceiveAction,
|
||||
@@ -24,7 +28,6 @@ import {
|
||||
} from '../../../common/abilityOption';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { BadDebtMenuItem } from './utils';
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,14 @@ function InvoiceDetailActionsBar({
|
||||
closeDrawer('invoice-detail-drawer');
|
||||
};
|
||||
|
||||
// Handle convert to invoice.
|
||||
const handleConvertToCreitNote = () => {
|
||||
history.push(`/credit-notes/new?from_invoice_id=${invoiceId}`, {
|
||||
invoiceId: invoiceId,
|
||||
});
|
||||
closeDrawer('invoice-detail-drawer');
|
||||
};
|
||||
|
||||
// Handle delete sale invoice.
|
||||
const handleDeleteInvoice = () => {
|
||||
openAlert('invoice-delete', { invoiceId });
|
||||
@@ -81,7 +92,7 @@ function InvoiceDetailActionsBar({
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={SaleInvoiceAction.Edit} a={AbilitySubject.Invoice}>
|
||||
<Button
|
||||
@@ -90,14 +101,13 @@ function InvoiceDetailActionsBar({
|
||||
text={<T id={'edit_invoice'} />}
|
||||
onClick={handleEditInvoice}
|
||||
/>
|
||||
|
||||
<NavbarDivider />
|
||||
</Can>
|
||||
<Can I={PaymentReceiveAction.Create} a={AbilitySubject.PaymentReceive}>
|
||||
<If condition={invoice.is_delivered && !invoice.is_fully_paid}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
||||
icon={<Icon icon="arrow-downward" iconSize={18} />}
|
||||
text={<T id={'add_payment'} />}
|
||||
onClick={handleQuickPaymentInvoice}
|
||||
/>
|
||||
@@ -120,19 +130,20 @@ function InvoiceDetailActionsBar({
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleDeleteInvoice}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</Can>
|
||||
<Can I={SaleInvoiceAction.Writeoff} a={AbilitySubject.Invoice}>
|
||||
<NavbarDivider />
|
||||
<BadDebtMenuItem
|
||||
payload={{
|
||||
onBadDebt: handleBadDebtInvoice,
|
||||
onCancelBadDebt: handleCancelBadDebtInvoice,
|
||||
onNotifyViaSMS: handleNotifyViaSMS,
|
||||
onConvert: handleConvertToCreitNote,
|
||||
}}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { DrawerBody } from 'components';
|
||||
|
||||
import 'style/components/Drawers/ViewDetail/ViewDetail.scss';
|
||||
|
||||
import InvoiceDetail from './InvoiceDetail';
|
||||
import { InvoiceDetailDrawerProvider } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Invoice detail drawer content.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function InvoiceDetailDrawerContent({
|
||||
// #ownProp
|
||||
|
||||
@@ -1,41 +1,30 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
||||
import { useTransactionsByReference, useInvoice } from 'hooks/query';
|
||||
import { useInvoice } from 'hooks/query';
|
||||
|
||||
const InvoiceDetailDrawerContext = React.createContext();
|
||||
/**
|
||||
* Invoice detail provider.
|
||||
*/
|
||||
function InvoiceDetailDrawerProvider({ invoiceId, ...props }) {
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: invoiceId,
|
||||
reference_type: 'SaleInvoice',
|
||||
},
|
||||
{ enabled: !!invoiceId },
|
||||
);
|
||||
|
||||
// Fetch sale invoice details.
|
||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||
enabled: !!invoiceId,
|
||||
});
|
||||
|
||||
//provider.
|
||||
// Provider.
|
||||
const provider = {
|
||||
transactions,
|
||||
invoiceId,
|
||||
invoice,
|
||||
};
|
||||
return (
|
||||
<DrawerLoading loading={isTransactionLoading || isInvoiceLoading}>
|
||||
<DrawerLoading loading={isInvoiceLoading}>
|
||||
<DrawerHeaderContent
|
||||
name="invoice-detail-drawer"
|
||||
title={intl.get('invoice_details')}
|
||||
title={intl.get('invoice_details.drawer.title', {
|
||||
invoiceNumber: invoice.invoice_no,
|
||||
})}
|
||||
/>
|
||||
<InvoiceDetailDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { T, TotalLines, FormatNumber, TotalLine } from 'components';
|
||||
import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss';
|
||||
import {
|
||||
CommercialDocFooter,
|
||||
T,
|
||||
If,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
} from 'components';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Invoice details footer.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function InvoiceDetailFooter() {
|
||||
const { invoice } = useInvoiceDetailDrawerContext();
|
||||
|
||||
if (!invoice.terms_conditions && !invoice.invoice_message) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={clsx(InvoiceDrawerCls.detail_panel_footer)}>
|
||||
<TotalLines>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.subtotal'} />}
|
||||
value={<FormatNumber value={invoice.balance} />}
|
||||
className={InvoiceDrawerCls.total_line_subtotal}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.total'} />}
|
||||
value={invoice.formatted_amount}
|
||||
className={InvoiceDrawerCls.total_line_total}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.payment_amount'} />}
|
||||
value={invoice.formatted_payment_amount}
|
||||
className={InvoiceDrawerCls.total_line_payment}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.due_amount'} />}
|
||||
value={invoice.formatted_due_amount}
|
||||
className={InvoiceDrawerCls.total_line_dueAmount}
|
||||
/>
|
||||
</TotalLines>
|
||||
</div>
|
||||
<CommercialDocFooter>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<If condition={invoice.terms_conditions}>
|
||||
<DetailItem label={<T id={'terms_conditions'} />}>
|
||||
{invoice.terms_conditions}
|
||||
</DetailItem>
|
||||
</If>
|
||||
|
||||
<If condition={invoice.invoice_message}>
|
||||
<DetailItem label={<T id={'invoice.details.invoice_message'} />}>
|
||||
{invoice.invoice_message}
|
||||
</DetailItem>
|
||||
</If>
|
||||
</DetailsMenu>
|
||||
</CommercialDocFooter>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { defaultTo } from 'lodash';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DetailsMenu, DetailItem, FormatDate } from 'components';
|
||||
import {
|
||||
ButtonLink,
|
||||
Row,
|
||||
Col,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
FormatDate,
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
CustomerDrawerLink,
|
||||
} from 'components';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss';
|
||||
import { InvoiceDetailsStatus } from './utils';
|
||||
|
||||
/**
|
||||
* Invoice detail header.
|
||||
@@ -14,44 +23,81 @@ import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss
|
||||
export default function InvoiceDetailHeader() {
|
||||
const { invoice } = useInvoiceDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(InvoiceDrawerCls.detail_panel_header)}>
|
||||
<DetailsMenu>
|
||||
<DetailItem label={intl.get('amount')}>
|
||||
<h3 class="big-number">{invoice.formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={intl.get('invoice_no')}
|
||||
children={invoice.invoice_no}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('customer_name')}
|
||||
children={invoice.customer?.display_name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('invoice_date')}
|
||||
children={<FormatDate value={invoice.invoice_date} />}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('due_date')}
|
||||
children={<FormatDate value={invoice.due_date} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
const handleCustomerLinkClick = () => {};
|
||||
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'140px'}>
|
||||
<DetailItem
|
||||
label={intl.get('due_amount')}
|
||||
children={invoice.formatted_due_amount}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(invoice.reference_no, '--')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('invoice.details.created_at')}
|
||||
children={<FormatDate value={invoice.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
return (
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<AmountDetailItem label={intl.get('amount')}>
|
||||
<h3 class="big-number">{invoice.formatted_amount}</h3>
|
||||
</AmountDetailItem>
|
||||
|
||||
<StatusDetailItem label={''}>
|
||||
<InvoiceDetailsStatus invoice={invoice} />
|
||||
</StatusDetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem label={intl.get('invoice_date')}>
|
||||
<FormatDate value={invoice.invoice_date} />
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('due_date')}>
|
||||
<FormatDate value={invoice.due_date} />
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('customer_name')}>
|
||||
<CustomerDrawerLink customerId={invoice.customer_id}>
|
||||
{invoice.customer?.display_name}
|
||||
</CustomerDrawerLink>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('invoice.details.invoice_no')}>
|
||||
{invoice.invoice_no}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'180px'}
|
||||
textAlign={'right'}
|
||||
>
|
||||
<DetailItem label={intl.get('due_amount')}>
|
||||
<strong>{invoice.formatted_due_amount}</strong>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={intl.get('invoice.details.payment_amount')}>
|
||||
<strong>{invoice.formatted_payment_amount}</strong>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(invoice.reference_no, '--')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('invoice.details.created_at')}
|
||||
children={<FormatDate value={invoice.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
`;
|
||||
|
||||
const AmountDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
`;
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { CommercialDocBox } from 'components';
|
||||
|
||||
import InvoiceDetailActionsBar from './InvoiceDetailActionsBar';
|
||||
import InvoiceDetailHeader from './InvoiceDetailHeader';
|
||||
import InvoiceDetailTable from './InvoiceDetailTable';
|
||||
import { InvoiceDetailTableFooter } from './InvoiceDetailTableFooter';
|
||||
import { InvoiceDetailFooter } from './InvoiceDetailFooter';
|
||||
|
||||
import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss';
|
||||
|
||||
/**
|
||||
* Invoice readonly details tab panel.
|
||||
*/
|
||||
export default function InvoiceDetailTab() {
|
||||
return (
|
||||
<div className={clsx(InvoiceDrawerCls.detail_panel)}>
|
||||
<InvoiceDetailActionsBar />
|
||||
|
||||
<Card>
|
||||
<InvoiceDetailHeader />
|
||||
<InvoiceDetailTable />
|
||||
<InvoiceDetailFooter />
|
||||
</Card>
|
||||
</div>
|
||||
<CommercialDocBox>
|
||||
<InvoiceDetailHeader />
|
||||
<InvoiceDetailTable />
|
||||
<InvoiceDetailTableFooter />
|
||||
<InvoiceDetailFooter />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { DataTable } from 'components';
|
||||
import { CommercialDocEntriesTable } from 'components';
|
||||
|
||||
import { useInvoiceReadonlyEntriesColumns } from './utils';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
import InvoiceDrawerCls from 'style/components/Drawers/InvoiceDrawer.module.scss';
|
||||
import { TableStyle } from '../../../common';
|
||||
|
||||
/**
|
||||
* Invoice readonly details entries table columns.
|
||||
*/
|
||||
export default function InvoiceDetailTable() {
|
||||
// Invoice readonly entries table columns.
|
||||
const columns = useInvoiceReadonlyEntriesColumns();
|
||||
|
||||
// Invoice details drawer context.
|
||||
const {
|
||||
invoice: { entries },
|
||||
} = useInvoiceDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={clsx(InvoiceDrawerCls.detail_panel_table)}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
</div>
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
FormatNumber,
|
||||
TotalLine,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from 'components';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Invoice details footer.
|
||||
*/
|
||||
export function InvoiceDetailTableFooter() {
|
||||
const { invoice } = useInvoiceDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<InvoiceDetailsFooterRoot>
|
||||
<InvoiceTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.subtotal'} />}
|
||||
value={<FormatNumber value={invoice.balance} />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.total'} />}
|
||||
value={invoice.formatted_amount}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.payment_amount'} />}
|
||||
value={invoice.formatted_payment_amount}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice.details.due_amount'} />}
|
||||
value={invoice.formatted_due_amount}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</InvoiceTotalLines>
|
||||
</InvoiceDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const InvoiceDetailsFooterRoot = styled.div``;
|
||||
|
||||
const InvoiceTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
|
||||
import { useTransactionsByReference } from 'hooks/query';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
import JournalEntriesTable, {
|
||||
AmountDisplayedBaseCurrencyMessage,
|
||||
} from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
|
||||
/**
|
||||
* Invoice GL entries table.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function InvoiceGLEntriesTable() {
|
||||
const { invoiceId } = useInvoiceDetailDrawerContext();
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: invoiceId,
|
||||
reference_type: 'SaleInvoice',
|
||||
},
|
||||
{ enabled: !!invoiceId },
|
||||
);
|
||||
|
||||
return (
|
||||
<InvoiceGLEntriesRoot>
|
||||
<AmountDisplayedBaseCurrencyMessage />
|
||||
<InvoiceGLEntriesDatatable
|
||||
loading={isTransactionLoading}
|
||||
transactions={transactions}
|
||||
/>
|
||||
</InvoiceGLEntriesRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const InvoiceGLEntriesDatatable = styled(JournalEntriesTable)``;
|
||||
|
||||
const InvoiceGLEntriesRoot = styled(Card)``;
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { DataTable, Card } from 'components';
|
||||
|
||||
import {
|
||||
useInvoicePaymentTransactionsColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
import { useInvoiceDetailDrawerContext } from '../InvoiceDetailDrawerProvider';
|
||||
import { useInvoicePaymentTransactions } from 'hooks/query';
|
||||
|
||||
import { TableStyle } from '../../../../common';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoice payment transactions datatable.
|
||||
*/
|
||||
function InvoicePaymentTransactionsTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// Invoice payment transactions table columns.
|
||||
const columns = useInvoicePaymentTransactionsColumns();
|
||||
|
||||
// Invoice drawer context.
|
||||
const { invoiceId } = useInvoiceDetailDrawerContext();
|
||||
|
||||
// Fetch invoice payment transactions.
|
||||
const {
|
||||
data: paymentTransactions,
|
||||
isFetching: isPaymentTransactionFetching,
|
||||
isLoading: isPaymentTransactionLoading,
|
||||
} = useInvoicePaymentTransactions(invoiceId, {
|
||||
enabled: !!invoiceId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ payment_receive_id }) => {
|
||||
openAlert('payment-receive-delete', {
|
||||
paymentReceiveId: payment_receive_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ payment_receive_id }) => {
|
||||
history.push(`/payment-receives/${payment_receive_id}/edit`);
|
||||
closeDrawer('invoice-detail-drawer');
|
||||
};
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isPaymentTransactionLoading}
|
||||
headerLoading={isPaymentTransactionLoading}
|
||||
progressBarLoading={isPaymentTransactionFetching}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(InvoicePaymentTransactionsTable);
|
||||
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../common/classes';
|
||||
import { FormatDateCell, Icon, Can } from '../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
PaymentReceiveAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={PaymentReceiveAction.Edit} a={AbilitySubject.PaymentReceive}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={PaymentReceiveAction.Delete} a={AbilitySubject.PaymentReceive}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve invoice payment transactions table columns.
|
||||
*/
|
||||
export const useInvoicePaymentTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'date',
|
||||
Header: intl.get('payment_date'),
|
||||
accessor: 'formatted_payment_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 110,
|
||||
className: 'date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'deposit_account_name',
|
||||
Header: intl.get('invoice_transactions.column.withdrawal_account'),
|
||||
accessor: 'deposit_account_name',
|
||||
width: 120,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_payment_amount',
|
||||
align: 'right',
|
||||
width: 120,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_number.',
|
||||
Header: intl.get('payment_no'),
|
||||
accessor: 'payment_number',
|
||||
width: 100,
|
||||
className: 'payment_number',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_reference_no',
|
||||
Header: intl.get('reference_no'),
|
||||
accessor: 'payment_reference_no',
|
||||
width: 90,
|
||||
className: 'payment_reference_no',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -21,7 +21,7 @@ function InvoiceDetailDrawer({
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
style={{ minWidth: '700px', maxWidth: '1000px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
@@ -7,9 +8,20 @@ import {
|
||||
Position,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Intent,
|
||||
Tag,
|
||||
} from '@blueprintjs/core';
|
||||
import { Icon, FormattedMessage as T, Choose } from 'components';
|
||||
import { FormatNumberCell } from '../../../components';
|
||||
import {
|
||||
FormatNumberCell,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
Choose,
|
||||
Can,
|
||||
} from 'components';
|
||||
import {
|
||||
SaleInvoiceAction,
|
||||
AbilitySubject,
|
||||
} from '../../../common/abilityOption';
|
||||
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
@@ -59,8 +71,12 @@ export const useInvoiceReadonlyEntriesColumns = () =>
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Invoice details more actions menu.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export const BadDebtMenuItem = ({
|
||||
payload: { onCancelBadDebt, onBadDebt, onNotifyViaSMS },
|
||||
payload: { onCancelBadDebt, onBadDebt, onNotifyViaSMS, onConvert },
|
||||
}) => {
|
||||
const { invoice } = useInvoiceDetailDrawerContext();
|
||||
|
||||
@@ -88,10 +104,18 @@ export const BadDebtMenuItem = ({
|
||||
/>
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
<MenuItem
|
||||
onClick={onNotifyViaSMS}
|
||||
text={<T id={'notify_via_sms.dialog.notify_via_sms'} />}
|
||||
/>
|
||||
<Can I={SaleInvoiceAction.Edit} a={AbilitySubject.Invoice}>
|
||||
<MenuItem
|
||||
onClick={onConvert}
|
||||
text={<T id={'invoice.convert_to_credit_note'} />}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleInvoiceAction.NotifyBySms} a={AbilitySubject.Invoice}>
|
||||
<MenuItem
|
||||
onClick={onNotifyViaSMS}
|
||||
text={<T id={'notify_via_sms.dialog.notify_via_sms'} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
@@ -99,3 +123,44 @@ export const BadDebtMenuItem = ({
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoice details status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function InvoiceDetailsStatus({ invoice }) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={invoice.is_fully_paid && invoice.is_delivered}>
|
||||
<StatusTag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'paid'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={invoice.is_delivered}>
|
||||
<Choose>
|
||||
<Choose.When condition={invoice.is_overdue}>
|
||||
<StatusTag intent={Intent.WARNING} round={true}>
|
||||
<T id={'overdue'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag intent={Intent.PRIMARY} round={true}>
|
||||
<T id={'delivered'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag round={true} minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusTag = styled(Tag)`
|
||||
min-width: 65px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import ItemDetailTab from './ItemDetailTab';
|
||||
import ItemDetailActionsBar from './ItemDetailActionsBar';
|
||||
import ItemDetailHeader from './ItemDetailHeader';
|
||||
|
||||
import { Card } from 'components';
|
||||
|
||||
/**
|
||||
* Item detail.
|
||||
@@ -12,10 +10,7 @@ export default function ItemDetail() {
|
||||
return (
|
||||
<div className="item-drawer">
|
||||
<ItemDetailActionsBar />
|
||||
|
||||
<Card>
|
||||
<ItemDetailHeader />
|
||||
</Card>
|
||||
<ItemDetailTab />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ const ItemDetailDrawerContext = React.createContext();
|
||||
* Item detail provider
|
||||
*/
|
||||
function ItemDetailDrawerProvider({ itemId, ...props }) {
|
||||
// transaction type payload.
|
||||
const [value, setValue] = React.useState('invoices');
|
||||
|
||||
// Fetches the given item detail.
|
||||
const { isLoading: isItemLoading, data: item } = useItem(itemId, {
|
||||
enabled: !!itemId,
|
||||
@@ -18,6 +21,8 @@ function ItemDetailDrawerProvider({ itemId, ...props }) {
|
||||
item,
|
||||
itemId,
|
||||
isItemLoading,
|
||||
value,
|
||||
setValue,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ import intl from 'react-intl-universal';
|
||||
import { defaultTo } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { If, DetailsMenu, DetailItem } from 'components';
|
||||
import { If, DetailsMenu, DetailItem, Card } from 'components';
|
||||
import { useItemDetailDrawerContext } from './ItemDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
@@ -13,70 +13,72 @@ export default function ItemDetailHeader() {
|
||||
const { item } = useItemDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<div class="item-drawer__content">
|
||||
<DetailsMenu direction={'vertical'}>
|
||||
<DetailItem
|
||||
name={'name'}
|
||||
label={intl.get('item_name')}
|
||||
children={item.name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('sell_price')}
|
||||
children={item.sell_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_price')}
|
||||
children={item.cost_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
|
||||
<DetailsMenu direction={'horizantal'}>
|
||||
<DetailItem label={intl.get('item_type')} children={item.type} />
|
||||
<DetailItem
|
||||
label={intl.get('item_code')}
|
||||
children={defaultTo(item.code, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<DetailItem name={'quantity'} label={intl.get('quantity_on_hand')}>
|
||||
<span
|
||||
className={classNames({
|
||||
mines: item.quantity_on_hand <= 0,
|
||||
plus: item.quantity_on_hand > 0,
|
||||
})}
|
||||
>
|
||||
{defaultTo(item.quantity_on_hand, '-')}
|
||||
</span>
|
||||
</DetailItem>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('category_name')}
|
||||
children={defaultTo(item.category?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('sell_account_id')}
|
||||
children={defaultTo(item?.sell_account?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_account_id')}
|
||||
children={defaultTo(item.cost_account?.name, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<Card>
|
||||
<div class="item-drawer__content">
|
||||
<DetailsMenu direction={'vertical'}>
|
||||
<DetailItem
|
||||
label={intl.get('inventory_account')}
|
||||
children={defaultTo(item?.inventory_account?.name, '-')}
|
||||
name={'name'}
|
||||
label={intl.get('item_name')}
|
||||
children={item.name}
|
||||
/>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('item.sell_description')}
|
||||
children={defaultTo(item.sell_description, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('item.purchase_description')}
|
||||
children={defaultTo(item.cost_description, '-')}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
<DetailItem
|
||||
label={intl.get('sell_price')}
|
||||
children={item.sell_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_price')}
|
||||
children={item.cost_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
|
||||
<DetailsMenu direction={'horizantal'}>
|
||||
<DetailItem label={intl.get('item_type')} children={item.type_formatted} />
|
||||
<DetailItem
|
||||
label={intl.get('item_code')}
|
||||
children={defaultTo(item.code, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<DetailItem name={'quantity'} label={intl.get('quantity_on_hand')}>
|
||||
<span
|
||||
className={classNames({
|
||||
mines: item.quantity_on_hand <= 0,
|
||||
plus: item.quantity_on_hand > 0,
|
||||
})}
|
||||
>
|
||||
{defaultTo(item.quantity_on_hand, '-')}
|
||||
</span>
|
||||
</DetailItem>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('category_name')}
|
||||
children={defaultTo(item.category?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('sell_account_id')}
|
||||
children={defaultTo(item?.sell_account?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_account_id')}
|
||||
children={defaultTo(item.cost_account?.name, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<DetailItem
|
||||
label={intl.get('inventory_account')}
|
||||
children={defaultTo(item?.inventory_account?.name, '-')}
|
||||
/>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('item.sell_description')}
|
||||
children={defaultTo(item.sell_description, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('item.purchase_description')}
|
||||
children={defaultTo(item.purchase_description, '-')}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
23
src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js
Normal file
23
src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import { DrawerMainTabs, FormattedMessage as T } from 'components';
|
||||
import { ItemPaymentTransactions } from './ItemPaymentTransactions';
|
||||
import ItemDetailHeader from './ItemDetailHeader';
|
||||
|
||||
|
||||
export default function ItemDetailTab() {
|
||||
return (
|
||||
<DrawerMainTabs renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id={'overview'}
|
||||
title={<T id={'overview'} />}
|
||||
panel={<ItemDetailHeader />}
|
||||
/>
|
||||
<Tab
|
||||
id={'transactions'}
|
||||
title={<T id={'transactions'} />}
|
||||
panel={<ItemPaymentTransactions />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../../common/classes';
|
||||
import { Can, FormatDateCell, Icon } from '../../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
BillAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={BillAction.Delete} a={AbilitySubject.Bill}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bill transactions associated with item table columns.
|
||||
*/
|
||||
export const useBillTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'bill_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_bill_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'bill_date',
|
||||
},
|
||||
{
|
||||
id: 'vendor',
|
||||
Header: intl.get('vendor'),
|
||||
accessor: 'vendor_display_name',
|
||||
width: 140,
|
||||
className: 'vendor',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'bill_number',
|
||||
Header: intl.get('bill_number'),
|
||||
accessor: (row) => (row.bill_number ? `${row.bill_number}` : null),
|
||||
width: 100,
|
||||
className: 'bill_number',
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable } from '../../../../../components';
|
||||
import { TableStyle } from 'common';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'
|
||||
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedBillTransactions } from 'hooks/query';
|
||||
import { useBillTransactionsColumns, ActionsMenu } from './components';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bill payment transactions data table.
|
||||
*/
|
||||
function BillPaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useBillTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch Estimate associated transactions.
|
||||
const {
|
||||
isLoading: isBillTransactionsLoading,
|
||||
isFetching: isBillTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedBillTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ bill_id }) => {
|
||||
openAlert('bill-delete', {
|
||||
billId: bill_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ bill_id }) => {
|
||||
history.push(`/bills/${bill_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isBillTransactionsLoading}
|
||||
headerLoading={isBillTransactionsLoading}
|
||||
progressBarLoading={isBillTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(BillPaymentTransactions);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../../common/classes';
|
||||
import { Can, FormatDateCell, Icon } from '../../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
SaleEstimateAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleEstimateAction.Edit} a={AbilitySubject.Estimate}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleEstimateAction.Delete} a={AbilitySubject.Estimate}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate transactions associated with item table columns.
|
||||
*/
|
||||
export const useEstimateTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'estimate_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_estimate_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'estimate_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'estimate_number',
|
||||
Header: intl.get('estimate_no'),
|
||||
accessor: 'estimate_number',
|
||||
width: 120,
|
||||
className: 'estimate_number',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable } from '../../../../../components';
|
||||
import { TableStyle } from 'common';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedEstimateTransactions } from 'hooks/query';
|
||||
import { useEstimateTransactionsColumns, ActionsMenu } from './components';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Esimtate payment transactions.
|
||||
*/
|
||||
function EstimatePaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useEstimateTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch Estimate associated transactions.
|
||||
const {
|
||||
isLoading: isEstimateTransactionsLoading,
|
||||
isFetching: isEstimateTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedEstimateTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ estimate_id }) => {
|
||||
openAlert('estimate-delete', {
|
||||
estimateId: estimate_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ estimate_id }) => {
|
||||
history.push(`/estimates/${estimate_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isEstimateTransactionsLoading}
|
||||
headerLoading={isEstimateTransactionsLoading}
|
||||
progressBarLoading={isEstimateTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(EstimatePaymentTransactions);
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../../common/classes';
|
||||
import { Can, FormatDateCell, Icon } from '../../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
SaleInvoiceAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleInvoiceAction.Edit} a={AbilitySubject.Invoice}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleInvoiceAction.Delete} a={AbilitySubject.Invoice}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve invoice payment transactions associated with item table columns.
|
||||
*/
|
||||
export const useInvoicePaymentTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'invoice_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_invoice_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'invoice_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'invoice_no',
|
||||
Header: intl.get('invoice_no__'),
|
||||
accessor: 'invoice_number',
|
||||
width: 120,
|
||||
className: 'invoice_no',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable } from '../../../../../components';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'
|
||||
|
||||
import { useItemAssociatedInvoiceTransactions } from 'hooks/query';
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import {
|
||||
useInvoicePaymentTransactionsColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
import { TableStyle } from 'common';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoice payment transactions.
|
||||
*/
|
||||
function InvoicePaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useInvoicePaymentTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch invoice associated transactions.
|
||||
const {
|
||||
isLoading: isInvoiceTransactionsLoading,
|
||||
isFetching: isInvoiceTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedInvoiceTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ invoice_id }) => {
|
||||
openAlert('invoice-delete', {
|
||||
invoiceId: invoice_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ invoice_id }) => {
|
||||
history.push(`/invoices/${invoice_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isInvoiceTransactionsLoading}
|
||||
headerLoading={isInvoiceTransactionsLoading}
|
||||
progressBarLoading={isInvoiceTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(InvoicePaymentTransactions);
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import InvoicePaymentTransactions from './InvoicePaymentTransactions';
|
||||
import EstimatePaymentTransactions from './EstimatePaymentTransactions';
|
||||
import ReceiptPaymentTransactions from './ReceiptPaymentTransactions';
|
||||
import BillPaymentTransactions from './BillPaymentTransactions';
|
||||
|
||||
export default function ItemPaymentTransactionsContent({ tansactionType }) {
|
||||
const handleType = () => {
|
||||
switch (tansactionType) {
|
||||
case 'invoices':
|
||||
default:
|
||||
return <InvoicePaymentTransactions />;
|
||||
case 'estimates':
|
||||
return <EstimatePaymentTransactions />;
|
||||
case 'receipts':
|
||||
return <ReceiptPaymentTransactions />;
|
||||
case 'bills':
|
||||
return <BillPaymentTransactions />;
|
||||
}
|
||||
};
|
||||
return handleType();
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '../../../../../common/classes';
|
||||
import { Can, FormatDateCell, Icon } from '../../../../../components';
|
||||
import { safeCallback } from 'utils';
|
||||
import {
|
||||
SaleReceiptAction,
|
||||
AbilitySubject,
|
||||
} from '../../../../../common/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleReceiptAction.Edit} a={AbilitySubject.Receipt}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleReceiptAction.Edit} a={AbilitySubject.Receipt}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve receipt transactions associated with item table columns.
|
||||
*/
|
||||
export const useReceiptTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'receipt_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_receipt_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'receipt_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'receipt_number',
|
||||
Header: intl.get('receipt_no'),
|
||||
accessor: 'receip_number',
|
||||
width: 120,
|
||||
className: 'receipt_number',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable } from '../../../../../components';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'
|
||||
import { TableStyle } from 'common';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedReceiptTransactions } from 'hooks/query';
|
||||
import { useReceiptTransactionsColumns, ActionsMenu } from './components';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Receipt payment transactions.
|
||||
*/
|
||||
function ReceiptPaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useReceiptTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch receipts associated transactions.
|
||||
const {
|
||||
isLoading: isReceiptTransactionsLoading,
|
||||
isFetching: isReceiptTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedReceiptTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ receipt_id }) => {
|
||||
openAlert('receipt-delete', {
|
||||
receiptId: receipt_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ receipt_id }) => {
|
||||
history.push(`/receipts/${receipt_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isReceiptTransactionsLoading}
|
||||
headerLoading={isReceiptTransactionsLoading}
|
||||
progressBarLoading={isReceiptTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(ReceiptPaymentTransactions);
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { ItemManuTransaction } from './utils';
|
||||
import ItemPaymentTransactionContent from './ItemPaymentTransactionContent';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../ItemDetailDrawerProvider';
|
||||
|
||||
export function ItemPaymentTransactions() {
|
||||
const { value } = useItemDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ItemTransactionsHeader />
|
||||
<ItemPaymentTransactionContent tansactionType={value} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Item transactions header.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function ItemTransactionsHeader() {
|
||||
const { setValue } = useItemDetailDrawerContext();
|
||||
|
||||
// handle item change.
|
||||
const handleItemChange = (item) => {
|
||||
setValue(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<ItemTransactionsHeaderRoot>
|
||||
<ItemManuTransaction onChange={handleItemChange} />
|
||||
</ItemTransactionsHeaderRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const ItemTransactionsHeaderRoot = styled.div`
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { useItemDetailDrawerContext } from '../ItemDetailDrawerProvider';
|
||||
import { useGetItemPaymentTransactionsMenu } from '../../../../common/itemPaymentTranactionsOption';
|
||||
|
||||
export const ItemManuTransaction = ({ onChange }) => {
|
||||
const { value, setValue } = useItemDetailDrawerContext();
|
||||
const itemTransactionMenu = useGetItemPaymentTransactionsMenu();
|
||||
|
||||
if (itemTransactionMenu.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClickItem = (item) => {
|
||||
onChange && onChange(item);
|
||||
};
|
||||
|
||||
const content = itemTransactionMenu.map(({ name, label }) => (
|
||||
<MenuItem onClick={() => handleClickItem(name)} text={label} />
|
||||
));
|
||||
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={<Menu>{content}</Menu>}
|
||||
>
|
||||
<ItemSwitchButton
|
||||
minimal={true}
|
||||
text={<T id={'item.drawer_transactions_by'} />}
|
||||
rightIcon={'caret-down'}
|
||||
>
|
||||
<ItemSwitchText>
|
||||
<T id={value} />
|
||||
</ItemSwitchText>
|
||||
</ItemSwitchButton>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemSwitchButton = styled(Button)`
|
||||
.bp3-button-text {
|
||||
display: flex;
|
||||
color: #727983;
|
||||
}
|
||||
`;
|
||||
|
||||
const ItemSwitchText = styled.span`
|
||||
font-weight: 600;
|
||||
color: #33304a;
|
||||
padding-left: 3px;
|
||||
`;
|
||||
@@ -19,7 +19,12 @@ function ItemDetailDrawer({
|
||||
payload: { itemId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer isOpen={isOpen} name={name} size={'750px'}>
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
<ItemDetailDrawerContent itemId={itemId} />
|
||||
</DrawerSuspense>
|
||||
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
Intent,
|
||||
NavbarDivider,
|
||||
} from '@blueprintjs/core';
|
||||
import { Can, FormattedMessage as T } from 'components';
|
||||
import { DrawerActionsBar, Can, FormattedMessage as T } from 'components';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
@@ -47,7 +46,7 @@ function ManualJournalDrawerActionBar({
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={ManualJournalAction.Edit} a={AbilitySubject.ManualJournal}>
|
||||
<Button
|
||||
@@ -68,7 +67,7 @@ function ManualJournalDrawerActionBar({
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from 'components';
|
||||
import { CommercialDocBox } from '../../../components';
|
||||
|
||||
import ManualJournalDrawerActionBar from './ManualJournalDrawerActionBar';
|
||||
import ManualJournalDrawerHeader from './ManualJournalDrawerHeader';
|
||||
import ManualJournalDrawerTable from './ManualJournalDrawerTable';
|
||||
import ManualJournalDrawerFooter from './ManualJournalDrawerFooter';
|
||||
|
||||
import { useManualJournalDrawerContext } from 'containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider';
|
||||
|
||||
/**
|
||||
* Manual journal view details.
|
||||
*/
|
||||
export default function ManualJournalDrawerDetails() {
|
||||
const { manualJournal } = useManualJournalDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={'journal-drawer'}>
|
||||
<ManualJournalDrawerActionBar manualJournal={manualJournal} />
|
||||
<ManualJournalDetailsRoot>
|
||||
<ManualJournalDrawerActionBar />
|
||||
|
||||
<div className="journal-drawer__content">
|
||||
<Card>
|
||||
<ManualJournalDrawerHeader />
|
||||
<ManualJournalDrawerTable />
|
||||
<ManualJournalDrawerFooter />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<CommercialDocBox>
|
||||
<ManualJournalDrawerHeader />
|
||||
<ManualJournalDrawerTable />
|
||||
<ManualJournalDrawerFooter />
|
||||
</CommercialDocBox>
|
||||
</ManualJournalDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const ManualJournalDetailsRoot = styled.div``;
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { T, FormatNumber } from '../../../components';
|
||||
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
|
||||
import {
|
||||
TRDarkSingleLine,
|
||||
TRDarkDoubleLines,
|
||||
T,
|
||||
FormatNumber,
|
||||
Table,
|
||||
TD,
|
||||
} from '../../../components';
|
||||
|
||||
/**
|
||||
* Manual journal readonly details footer.
|
||||
@@ -13,26 +21,41 @@ export default function ManualJournalDrawerFooter() {
|
||||
|
||||
return (
|
||||
<div className="journal-drawer__content-footer">
|
||||
<div class="total-lines">
|
||||
<div class="total-lines__line total-lines__line--subtotal">
|
||||
<div class="title">
|
||||
<JournalTotalTable>
|
||||
<TRDarkSingleLine>
|
||||
<TDLabel>
|
||||
<T id={'manual_journal.details.subtotal'} />
|
||||
</div>
|
||||
<div class="debit">
|
||||
</TDLabel>
|
||||
<TDAmount textAlign={'right'}>
|
||||
<FormatNumber value={amount} />
|
||||
</div>
|
||||
<div class="credit">
|
||||
</TDAmount>
|
||||
<TDAmount textAlign={'right'}>
|
||||
<FormatNumber value={amount} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="total-lines__line total-lines__line--total">
|
||||
<div class="title">
|
||||
</TDAmount>
|
||||
</TRDarkSingleLine>
|
||||
|
||||
<TRDarkDoubleLines>
|
||||
<TDLabel>
|
||||
<T id={'manual_journal.details.total'} />
|
||||
</div>
|
||||
<div class="debit">{formatted_amount}</div>
|
||||
<div class="credit">{formatted_amount}</div>
|
||||
</div>
|
||||
</div>
|
||||
</TDLabel>
|
||||
<TDAmount textAlign={'right'}>{formatted_amount}</TDAmount>
|
||||
<TDAmount textAlign={'right'}>{formatted_amount}</TDAmount>
|
||||
</TRDarkDoubleLines>
|
||||
</JournalTotalTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const JournalTotalTable = styled(Table)`
|
||||
font-weight: 600;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const TDLabel = styled(TD)`
|
||||
width: 220px;
|
||||
`;
|
||||
|
||||
const TDAmount = styled(TD)`
|
||||
width: 155px;
|
||||
`;
|
||||
|
||||
@@ -1,53 +1,71 @@
|
||||
import React from 'react';
|
||||
import { defaultTo } from 'lodash';
|
||||
import { DetailsMenu, DetailItem, FormattedMessage as T } from 'components';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
FormattedMessage as T,
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
} from 'components';
|
||||
import { ManualJournalDetailsStatus } from './utils';
|
||||
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
|
||||
|
||||
/**
|
||||
* Manual journal details header.
|
||||
*/
|
||||
export default function ManualJournalDrawerHeader() {
|
||||
const {
|
||||
manualJournal: {
|
||||
formatted_amount,
|
||||
journal_type,
|
||||
journal_number,
|
||||
reference,
|
||||
currency_code,
|
||||
description,
|
||||
},
|
||||
} = useManualJournalDrawerContext();
|
||||
const { manualJournal } = useManualJournalDrawerContext();
|
||||
|
||||
return (
|
||||
<div className={'journal-drawer__content-header'}>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'total'} label={<T id={'total'} />}>
|
||||
<h3 class="amount">{formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<DetailItem name={'total'} label={<T id={'total'} />}>
|
||||
<h3 class="big-number">{manualJournal.formatted_amount}</h3>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'journal-type'} label={<T id={'journal_type'} />}>
|
||||
{journal_type}
|
||||
</DetailItem>
|
||||
<StatusDetailItem>
|
||||
<ManualJournalDetailsStatus manualJournal={manualJournal} />
|
||||
</StatusDetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
|
||||
<DetailItem name={'journal-number'} label={<T id={'journal_no'} />}>
|
||||
{journal_number}
|
||||
</DetailItem>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem name={'journal-type'} label={<T id={'journal_type'} />}>
|
||||
{manualJournal.journal_type}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'reference-no'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(reference, '-')}
|
||||
</DetailItem>
|
||||
<DetailItem name={'journal-number'} label={<T id={'journal_no'} />}>
|
||||
{manualJournal.journal_number}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem name={'currency'} label={<T id={'currency'} />}>
|
||||
{currency_code}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
<DetailItem name={'reference-no'} label={<T id={'reference_no'} />}>
|
||||
{defaultTo(manualJournal.reference, '-')}
|
||||
</DetailItem>
|
||||
|
||||
<div class="journal-drawer__content-description">
|
||||
<b class="title">
|
||||
<T id={'manual_journal.details.description'} />
|
||||
</b>
|
||||
: {defaultTo(description, '—')}
|
||||
</div>
|
||||
</div>
|
||||
<DetailItem name={'currency'} label={<T id={'currency'} />}>
|
||||
{manualJournal.currency_code}
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label={<T id={'description'} />}>
|
||||
{defaultTo(manualJournal.description, '—')}
|
||||
</DetailItem>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
`;
|
||||
|
||||
@@ -31,7 +31,7 @@ function ManualJournalDrawerProvider({ manualJournalId, ...props }) {
|
||||
<DrawerLoading loading={isJournalLoading}>
|
||||
<DrawerHeaderContent
|
||||
name={'journal-drawer'}
|
||||
title={intl.get('manual_journal_number', {
|
||||
title={intl.get('manual_journal.drawer.title', {
|
||||
number: manualJournal?.journal_number,
|
||||
})}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user