mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
Merge branch 'feature/multi-dimensions' of https://github.com/bigcapitalhq/client into feature/multi-dimensions
This commit is contained in:
@@ -13,8 +13,9 @@ function VendorDrawerLinkComponent({
|
|||||||
openDrawer,
|
openDrawer,
|
||||||
}) {
|
}) {
|
||||||
// Handle view customer drawer.
|
// Handle view customer drawer.
|
||||||
const handleVendorDrawer = () => {
|
const handleVendorDrawer = (event) => {
|
||||||
openDrawer('vendor-details-drawer', { vendorId });
|
openDrawer('vendor-details-drawer', { vendorId });
|
||||||
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
return <ButtonLink onClick={handleVendorDrawer}>{children}</ButtonLink>;
|
return <ButtonLink onClick={handleVendorDrawer}>{children}</ButtonLink>;
|
||||||
|
|||||||
@@ -8,29 +8,37 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { useBillTotals } from './utils';
|
||||||
|
|
||||||
export function BillFormFooterRight() {
|
export function BillFormFooterRight() {
|
||||||
|
const {
|
||||||
|
formattedSubtotal,
|
||||||
|
formattedTotal,
|
||||||
|
formattedDueTotal,
|
||||||
|
formattedPaymentTotal,
|
||||||
|
} = useBillTotals();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'bill.details.subtotal'} />}
|
title={<T id={'bill.details.subtotal'} />}
|
||||||
value={'$5000.00'}
|
value={formattedSubtotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'bill.details.total'} />}
|
title={<T id={'bill.details.total'} />}
|
||||||
value={'$5000.00'}
|
value={formattedTotal}
|
||||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'bill.details.payment_amount'} />}
|
title={<T id={'bill.details.payment_amount'} />}
|
||||||
value={'$0.00'}
|
value={formattedPaymentTotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'bill.details.due_amount'} />}
|
title={<T id={'bill.details.due_amount'} />}
|
||||||
value={'$5000.00'}
|
value={formattedDueTotal}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
</BillTotalLines>
|
</BillTotalLines>
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import {
|
|||||||
VendorSelectField,
|
VendorSelectField,
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
Icon,
|
Icon,
|
||||||
ExchangeRateInputGroup,
|
CustomerDrawerLink,
|
||||||
If,
|
VendorDrawerLink,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { vendorsFieldShouldUpdate } from './utils';
|
import { vendorsFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
import { useBillFormContext } from './BillFormProvider';
|
import { useBillFormContext } from './BillFormProvider';
|
||||||
import BillFormCurrencyTag from './BillFormCurrencyTag';
|
import { BillExchangeRateInputField } from './components';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import {
|
import {
|
||||||
momentFormatter,
|
momentFormatter,
|
||||||
@@ -37,13 +37,7 @@ import {
|
|||||||
*/
|
*/
|
||||||
function BillFormHeader() {
|
function BillFormHeader() {
|
||||||
// Bill form context.
|
// Bill form context.
|
||||||
const {
|
const { vendors } = useBillFormContext();
|
||||||
vendors,
|
|
||||||
isForeignVendor,
|
|
||||||
baseCurrency,
|
|
||||||
selectVendor,
|
|
||||||
setSelectVendor,
|
|
||||||
} = useBillFormContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
@@ -57,7 +51,11 @@ function BillFormHeader() {
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'vendor_name'} />}
|
label={<T id={'vendor_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames(CLASSES.FILL, 'form-group--vendor')}
|
className={classNames(
|
||||||
|
'form-group--customer-name',
|
||||||
|
'form-group--select-list',
|
||||||
|
CLASSES.FILL,
|
||||||
|
)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
helperText={<ErrorMessage name={'vendor_id'} />}
|
helperText={<ErrorMessage name={'vendor_id'} />}
|
||||||
@@ -70,26 +68,25 @@ function BillFormHeader() {
|
|||||||
onContactSelected={(contact) => {
|
onContactSelected={(contact) => {
|
||||||
form.setFieldValue('vendor_id', contact.id);
|
form.setFieldValue('vendor_id', contact.id);
|
||||||
form.setFieldValue('currency_code', contact?.currency_code);
|
form.setFieldValue('currency_code', contact?.currency_code);
|
||||||
setSelectVendor(contact);
|
|
||||||
}}
|
}}
|
||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
<BillFormCurrencyTag />
|
|
||||||
</ControlVendorGroup>
|
</ControlVendorGroup>
|
||||||
|
{value && (
|
||||||
|
<VendorButtonLink vendorId={value}>
|
||||||
|
View Vendor Details
|
||||||
|
</VendorButtonLink>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<If condition={isForeignVendor}>
|
<BillExchangeRateInputField
|
||||||
<ExchangeRateInputGroup
|
|
||||||
fromCurrency={baseCurrency}
|
|
||||||
toCurrency={selectVendor?.currency_code}
|
|
||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
{/* ------- Bill date ------- */}
|
{/* ------- Bill date ------- */}
|
||||||
<FastField name={'bill_date'}>
|
<FastField name={'bill_date'}>
|
||||||
@@ -184,3 +181,8 @@ const ControlVendorGroup = styled(ControlGroup)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transform: none;
|
transform: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const VendorButtonLink = styled(VendorDrawerLink)`
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|||||||
28
src/containers/Purchases/Bills/BillForm/components.js
Normal file
28
src/containers/Purchases/Bills/BillForm/components.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import { ExchangeRateInputGroup } from 'components';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { useBillIsForeignCustomer } from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bill exchange rate input field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function BillExchangeRateInputField({ ...props }) {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
const isForeignCustomer = useBillIsForeignCustomer();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignCustomer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,12 +11,14 @@ import {
|
|||||||
transformToForm,
|
transformToForm,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
orderingLinesIndexes,
|
orderingLinesIndexes,
|
||||||
|
formattedAmount,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import {
|
import {
|
||||||
updateItemsEntriesTotal,
|
updateItemsEntriesTotal,
|
||||||
ensureEntriesHaveEmptyLine,
|
ensureEntriesHaveEmptyLine,
|
||||||
} from 'containers/Entries/utils';
|
} from 'containers/Entries/utils';
|
||||||
import { isLandedCostDisabled } from '../../../Entries/utils';
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { isLandedCostDisabled, getEntriesTotal } from '../../../Entries/utils';
|
||||||
import { useBillFormContext } from './BillFormProvider';
|
import { useBillFormContext } from './BillFormProvider';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
@@ -216,3 +218,69 @@ export const useSetPrimaryWarehouseToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isWarehousesSuccess, setFieldValue, warehouses]);
|
}, [isWarehousesSuccess, setFieldValue, warehouses]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the bill totals.
|
||||||
|
*/
|
||||||
|
export const useBillTotals = () => {
|
||||||
|
const {
|
||||||
|
values: { entries, currency_code: currencyCode },
|
||||||
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Retrieves the bili entries total.
|
||||||
|
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
|
|
||||||
|
// Retrieves the formatted total money.
|
||||||
|
const formattedTotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted subtotal.
|
||||||
|
const formattedSubtotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode, { money: false }),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the payment total.
|
||||||
|
const paymentTotal = React.useMemo(() => 0, []);
|
||||||
|
|
||||||
|
// Retireves the formatted payment total.
|
||||||
|
const formattedPaymentTotal = React.useMemo(
|
||||||
|
() => formattedAmount(paymentTotal, currencyCode),
|
||||||
|
[paymentTotal, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted due total.
|
||||||
|
const dueTotal = React.useMemo(
|
||||||
|
() => total - paymentTotal,
|
||||||
|
[total, paymentTotal],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted due total.
|
||||||
|
const formattedDueTotal = React.useMemo(
|
||||||
|
() => formattedAmount(dueTotal, currencyCode),
|
||||||
|
[dueTotal, currencyCode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
paymentTotal,
|
||||||
|
dueTotal,
|
||||||
|
formattedTotal,
|
||||||
|
formattedSubtotal,
|
||||||
|
formattedPaymentTotal,
|
||||||
|
formattedDueTotal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the bill has foreign customer.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useBillIsForeignCustomer = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignCustomer = React.useMemo(
|
||||||
|
() => values.currency_code !== currentOrganization.base_currency,
|
||||||
|
[values.currency_code, currentOrganization.base_currency],
|
||||||
|
);
|
||||||
|
return isForeignCustomer;
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,18 +7,21 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { useCreditNoteTotals } from './utils';
|
||||||
|
|
||||||
export function CreditNoteFormFooterRight() {
|
export function CreditNoteFormFooterRight() {
|
||||||
|
const { formattedSubtotal, formattedTotal } = useCreditNoteTotals();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CreditNoteTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<CreditNoteTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'credit_note.drawer.label_subtotal'} />}
|
title={<T id={'credit_note.drawer.label_subtotal'} />}
|
||||||
value={'$5000.00'}
|
value={formattedSubtotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'credit_note.drawer.label_total'} />}
|
title={<T id={'credit_note.drawer.label_total'} />}
|
||||||
value={'$5000.00'}
|
value={formattedTotal}
|
||||||
// borderStyle={TotalLineBorderStyle.SingleDark}
|
// borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ import {
|
|||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
InputPrependButton,
|
InputPrependButton,
|
||||||
Icon,
|
Icon,
|
||||||
If,
|
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
ExchangeRateInputGroup,
|
CustomerDrawerLink,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import {
|
import {
|
||||||
customerNameFieldShouldUpdate,
|
customerNameFieldShouldUpdate,
|
||||||
@@ -27,7 +26,7 @@ import {
|
|||||||
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import CreditNotetFormCurrencyTag from './CreditNotetFormCurrencyTag';
|
import { CreditNoteExchangeRateInputField } from './components';
|
||||||
import {
|
import {
|
||||||
momentFormatter,
|
momentFormatter,
|
||||||
compose,
|
compose,
|
||||||
@@ -49,13 +48,7 @@ function CreditNoteFormHeaderFields({
|
|||||||
creditNextNumber,
|
creditNextNumber,
|
||||||
}) {
|
}) {
|
||||||
// Credit note form context.
|
// Credit note form context.
|
||||||
const {
|
const { customers } = useCreditNoteFormContext();
|
||||||
customers,
|
|
||||||
isForeignCustomer,
|
|
||||||
baseCurrency,
|
|
||||||
selectCustomer,
|
|
||||||
setSelectCustomer,
|
|
||||||
} = useCreditNoteFormContext();
|
|
||||||
|
|
||||||
// Handle credit number changing.
|
// Handle credit number changing.
|
||||||
const handleCreditNumberChange = () => {
|
const handleCreditNumberChange = () => {
|
||||||
@@ -108,25 +101,25 @@ function CreditNoteFormHeaderFields({
|
|||||||
onContactSelected={(customer) => {
|
onContactSelected={(customer) => {
|
||||||
form.setFieldValue('customer_id', customer.id);
|
form.setFieldValue('customer_id', customer.id);
|
||||||
form.setFieldValue('currency_code', customer?.currency_code);
|
form.setFieldValue('currency_code', customer?.currency_code);
|
||||||
setSelectCustomer(customer);
|
|
||||||
}}
|
}}
|
||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
</ControlCustomerGroup>
|
</ControlCustomerGroup>
|
||||||
|
{value && (
|
||||||
|
<CustomerButtonLink customerId={value}>
|
||||||
|
View Customer Details
|
||||||
|
</CustomerButtonLink>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<If condition={isForeignCustomer}>
|
<CreditNoteExchangeRateInputField
|
||||||
<ExchangeRateInputGroup
|
|
||||||
fromCurrency={baseCurrency}
|
|
||||||
toCurrency={selectCustomer?.currency_code}
|
|
||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
{/* ----------- Credit note date ----------- */}
|
{/* ----------- Credit note date ----------- */}
|
||||||
<FastField name={'credit_note_date'}>
|
<FastField name={'credit_note_date'}>
|
||||||
@@ -222,3 +215,7 @@ const ControlCustomerGroup = styled(ControlGroup)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transform: none;
|
transform: none;
|
||||||
`;
|
`;
|
||||||
|
const CustomerButtonLink = styled(CustomerDrawerLink)`
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import { ExchangeRateInputGroup } from 'components';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { useCreditNoteIsForeignCustomer } from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* credit exchange rate input field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function CreditNoteExchangeRateInputField({ ...props }) {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
const isForeignCustomer = useCreditNoteIsForeignCustomer();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignCustomer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
transformToForm,
|
transformToForm,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
transactionNumber,
|
transactionNumber,
|
||||||
|
formattedAmount,
|
||||||
orderingLinesIndexes,
|
orderingLinesIndexes,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
@@ -17,6 +18,8 @@ import {
|
|||||||
updateItemsEntriesTotal,
|
updateItemsEntriesTotal,
|
||||||
ensureEntriesHaveEmptyLine,
|
ensureEntriesHaveEmptyLine,
|
||||||
} from 'containers/Entries/utils';
|
} from 'containers/Entries/utils';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { getEntriesTotal } from 'containers/Entries/utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -168,3 +171,47 @@ export const useSetPrimaryWarehouseToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isWarehousesSuccess, setFieldValue, warehouses]);
|
}, [isWarehousesSuccess, setFieldValue, warehouses]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the credit note totals.
|
||||||
|
*/
|
||||||
|
export const useCreditNoteTotals = () => {
|
||||||
|
const {
|
||||||
|
values: { entries, currency_code: currencyCode },
|
||||||
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Retrieves the invoice entries total.
|
||||||
|
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
|
|
||||||
|
// Retrieves the formatted total money.
|
||||||
|
const formattedTotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted subtotal.
|
||||||
|
const formattedSubtotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode, { money: false }),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
formattedTotal,
|
||||||
|
formattedSubtotal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the receipt has foreign customer.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useCreditNoteIsForeignCustomer = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignCustomer = React.useMemo(
|
||||||
|
() => values.currency_code !== currentOrganization.base_currency,
|
||||||
|
[values.currency_code, currentOrganization.base_currency],
|
||||||
|
);
|
||||||
|
return isForeignCustomer;
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,18 +7,22 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { useEstimateTotals } from './utils';
|
||||||
|
|
||||||
export function EstimateFormFooterRight() {
|
export function EstimateFormFooterRight() {
|
||||||
|
|
||||||
|
const { formattedSubtotal, formattedTotal } = useEstimateTotals();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EstimateTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<EstimateTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'estimate.details.subtotal'} />}
|
title={<T id={'estimate.details.subtotal'} />}
|
||||||
value={'$5000.00'}
|
value={formattedSubtotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'estimate.details.total'} />}
|
title={<T id={'estimate.details.total'} />}
|
||||||
value={'$5000.00'}
|
value={formattedTotal}
|
||||||
// borderStyle={TotalLineBorderStyle.SingleDark}
|
// borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -23,16 +23,15 @@ import { CLASSES } from 'common/classes';
|
|||||||
import {
|
import {
|
||||||
CustomerSelectField,
|
CustomerSelectField,
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
If,
|
|
||||||
Icon,
|
Icon,
|
||||||
InputPrependButton,
|
InputPrependButton,
|
||||||
ExchangeRateInputGroup,
|
CustomerDrawerLink,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import EstimateFormCurrencyTag from './EstimateFormCurrencyTag';
|
|
||||||
import { useObserveEstimateNoSettings } from './utils';
|
import { useObserveEstimateNoSettings } from './utils';
|
||||||
|
import { EstimateExchangeRateInputField } from './components';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,13 +46,7 @@ function EstimateFormHeader({
|
|||||||
estimateNumberPrefix,
|
estimateNumberPrefix,
|
||||||
estimateNextNumber,
|
estimateNextNumber,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const { customers } = useEstimateFormContext();
|
||||||
customers,
|
|
||||||
isForeignCustomer,
|
|
||||||
baseCurrency,
|
|
||||||
selectCustomer,
|
|
||||||
setSelectCustomer,
|
|
||||||
} = useEstimateFormContext();
|
|
||||||
|
|
||||||
const handleEstimateNumberBtnClick = () => {
|
const handleEstimateNumberBtnClick = () => {
|
||||||
openDialog('estimate-number-form', {});
|
openDialog('estimate-number-form', {});
|
||||||
@@ -100,27 +93,26 @@ function EstimateFormHeader({
|
|||||||
onContactSelected={(customer) => {
|
onContactSelected={(customer) => {
|
||||||
form.setFieldValue('customer_id', customer.id);
|
form.setFieldValue('customer_id', customer.id);
|
||||||
form.setFieldValue('currency_code', customer?.currency_code);
|
form.setFieldValue('currency_code', customer?.currency_code);
|
||||||
setSelectCustomer(customer);
|
|
||||||
}}
|
}}
|
||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</ControlCustomerGroup>
|
</ControlCustomerGroup>
|
||||||
|
{value && (
|
||||||
|
<CustomerButtonLink customerId={value}>
|
||||||
|
View Customer Details
|
||||||
|
</CustomerButtonLink>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<If condition={isForeignCustomer}>
|
<EstimateExchangeRateInputField
|
||||||
<ExchangeRateInputGroup
|
|
||||||
fromCurrency={baseCurrency}
|
|
||||||
toCurrency={selectCustomer?.currency_code}
|
|
||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
{/* ----------- Estimate date ----------- */}
|
{/* ----------- Estimate date ----------- */}
|
||||||
<FastField name={'estimate_date'}>
|
<FastField name={'estimate_date'}>
|
||||||
@@ -246,3 +238,8 @@ const ControlCustomerGroup = styled(ControlGroup)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transform: none;
|
transform: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const CustomerButtonLink = styled(CustomerDrawerLink)`
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|||||||
29
src/containers/Sales/Estimates/EstimateForm/components.js
Normal file
29
src/containers/Sales/Estimates/EstimateForm/components.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import { ExchangeRateInputGroup } from 'components';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { useEstimateIsForeignCustomer } from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimate exchange rate input field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function EstimateExchangeRateInputField({ ...props }) {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
const isForeignCustomer = useEstimateIsForeignCustomer();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignCustomer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,12 +9,15 @@ import {
|
|||||||
transactionNumber,
|
transactionNumber,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
transformToForm,
|
transformToForm,
|
||||||
|
formattedAmount,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
import {
|
import {
|
||||||
updateItemsEntriesTotal,
|
updateItemsEntriesTotal,
|
||||||
ensureEntriesHaveEmptyLine,
|
ensureEntriesHaveEmptyLine,
|
||||||
} from 'containers/Entries/utils';
|
} from 'containers/Entries/utils';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { getEntriesTotal } from 'containers/Entries/utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -187,3 +190,47 @@ export const useSetPrimaryBranchToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the estimate totals.
|
||||||
|
*/
|
||||||
|
export const useEstimateTotals = () => {
|
||||||
|
const {
|
||||||
|
values: { entries, currency_code: currencyCode },
|
||||||
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Retrieves the invoice entries total.
|
||||||
|
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
|
|
||||||
|
// Retrieves the formatted total money.
|
||||||
|
const formattedTotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted subtotal.
|
||||||
|
const formattedSubtotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode, { money: false }),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
formattedTotal,
|
||||||
|
formattedSubtotal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the estimate has foreign customer.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useEstimateIsForeignCustomer = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignCustomer = React.useMemo(
|
||||||
|
() => values.currency_code !== currentOrganization.base_currency,
|
||||||
|
[values.currency_code, currentOrganization.base_currency],
|
||||||
|
);
|
||||||
|
return isForeignCustomer;
|
||||||
|
};
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ function InvoiceFormHeaderFields({
|
|||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
{/* <InvoiceCurrencyTag /> */}
|
|
||||||
</ControlCustomerGroup>
|
</ControlCustomerGroup>
|
||||||
{value && (
|
{value && (
|
||||||
<CustomerButtonLink customerId={value}>
|
<CustomerButtonLink customerId={value}>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export function PaymentReceiveFormFootetLeft() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{/* --------- Statement--------- */}
|
{/* --------- Statement--------- */}
|
||||||
<StatementFormGroup
|
<TermsConditsFormGroup
|
||||||
name={'statement'}
|
name={'statement'}
|
||||||
label={<T id={'statement'} />}
|
label={<T id={'statement'} />}
|
||||||
hintText={'Will be displayed on the Payment'}
|
hintText={'Will be displayed on the Payment'}
|
||||||
@@ -15,15 +15,13 @@ export function PaymentReceiveFormFootetLeft() {
|
|||||||
name={'statement'}
|
name={'statement'}
|
||||||
placeholder={'Thanks for your business and have a great day!'}
|
placeholder={'Thanks for your business and have a great day!'}
|
||||||
/>
|
/>
|
||||||
</StatementFormGroup>
|
</TermsConditsFormGroup>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatementFormGroup = styled(FFormGroup)`
|
const TermsConditsFormGroup = styled(FFormGroup)`
|
||||||
&.bp3-form-group {
|
&.bp3-form-group {
|
||||||
margin-bottom: 40px;
|
|
||||||
|
|
||||||
.bp3-label {
|
.bp3-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
@@ -33,3 +31,4 @@ const StatementFormGroup = styled(FFormGroup)`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -7,18 +7,21 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { usePaymentReceiveTotals } from './utils';
|
||||||
|
|
||||||
export function PaymentReceiveFormFootetRight() {
|
export function PaymentReceiveFormFootetRight() {
|
||||||
|
const { formattedSubtotal, formattedTotal } = usePaymentReceiveTotals();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentReceiveTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<PaymentReceiveTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'estimate.details.subtotal'} />}
|
title={<T id={'estimate.details.subtotal'} />}
|
||||||
value={'$5000.00'}
|
value={formattedSubtotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'estimate.details.total'} />}
|
title={<T id={'estimate.details.total'} />}
|
||||||
value={'$5000.00'}
|
value={formattedTotal}
|
||||||
// borderStyle={TotalLineBorderStyle.SingleDark}
|
// borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ import {
|
|||||||
InputPrependButton,
|
InputPrependButton,
|
||||||
MoneyInputGroup,
|
MoneyInputGroup,
|
||||||
InputPrependText,
|
InputPrependText,
|
||||||
ExchangeRateInputGroup,
|
CustomerDrawerLink,
|
||||||
Hint,
|
Hint,
|
||||||
Money,
|
Money,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||||
import PaymentReceiveFormCurrencyTag from './PaymentReceiveFormCurrencyTag';
|
import { PaymentReceiveExchangeRateInputField } from './components';
|
||||||
|
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
@@ -49,6 +49,7 @@ import {
|
|||||||
customersFieldShouldUpdate,
|
customersFieldShouldUpdate,
|
||||||
accountsFieldShouldUpdate,
|
accountsFieldShouldUpdate,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
import { toSafeInteger } from 'lodash';
|
import { toSafeInteger } from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,15 +68,7 @@ function PaymentReceiveHeaderFields({
|
|||||||
paymentReceiveNextNumber,
|
paymentReceiveNextNumber,
|
||||||
}) {
|
}) {
|
||||||
// Payment receive form context.
|
// Payment receive form context.
|
||||||
const {
|
const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
|
||||||
customers,
|
|
||||||
accounts,
|
|
||||||
isNewMode,
|
|
||||||
isForeignCustomer,
|
|
||||||
baseCurrency,
|
|
||||||
selectCustomer,
|
|
||||||
setSelectCustomer,
|
|
||||||
} = usePaymentReceiveFormContext();
|
|
||||||
|
|
||||||
// Formik form context.
|
// Formik form context.
|
||||||
const {
|
const {
|
||||||
@@ -154,7 +147,6 @@ function PaymentReceiveHeaderFields({
|
|||||||
form.setFieldValue('customer_id', customer.id);
|
form.setFieldValue('customer_id', customer.id);
|
||||||
form.setFieldValue('full_amount', '');
|
form.setFieldValue('full_amount', '');
|
||||||
form.setFieldValue('currency_code', customer?.currency_code);
|
form.setFieldValue('currency_code', customer?.currency_code);
|
||||||
setSelectCustomer(customer);
|
|
||||||
}}
|
}}
|
||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
disabled={!isNewMode}
|
disabled={!isNewMode}
|
||||||
@@ -164,20 +156,20 @@ function PaymentReceiveHeaderFields({
|
|||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
</ControlCustomerGroup>
|
</ControlCustomerGroup>
|
||||||
|
{value && (
|
||||||
|
<CustomerButtonLink customerId={value}>
|
||||||
|
View Customer Details
|
||||||
|
</CustomerButtonLink>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<If condition={isForeignCustomer}>
|
<PaymentReceiveExchangeRateInputField
|
||||||
<ExchangeRateInputGroup
|
|
||||||
fromCurrency={baseCurrency}
|
|
||||||
toCurrency={selectCustomer?.currency_code}
|
|
||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
{/* ------------- Payment date ------------- */}
|
{/* ------------- Payment date ------------- */}
|
||||||
<FastField name={'payment_date'}>
|
<FastField name={'payment_date'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
@@ -359,3 +351,8 @@ const ControlCustomerGroup = styled(ControlGroup)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transform: none;
|
transform: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const CustomerButtonLink = styled(CustomerDrawerLink)`
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
|
||||||
import { Money } from 'components';
|
import { Money, ExchangeRateInputGroup } from 'components';
|
||||||
import { MoneyFieldCell } from 'components/DataTableCells';
|
import { MoneyFieldCell } from 'components/DataTableCells';
|
||||||
|
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { useEstimateIsForeignCustomer } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice date cell.
|
* Invoice date cell.
|
||||||
*/
|
*/
|
||||||
@@ -76,3 +80,26 @@ export const usePaymentReceiveEntriesColumns = () => {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* payment receive exchange rate input field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function PaymentReceiveExchangeRateInputField({ ...props }) {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
const isForeignCustomer = useEstimateIsForeignCustomer();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignCustomer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import {
|
|||||||
transformToForm,
|
transformToForm,
|
||||||
safeSumBy,
|
safeSumBy,
|
||||||
orderingLinesIndexes,
|
orderingLinesIndexes,
|
||||||
|
formattedAmount,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { getEntriesTotal } from 'containers/Entries/utils';
|
||||||
|
|
||||||
// Default payment receive entry.
|
// Default payment receive entry.
|
||||||
export const defaultPaymentReceiveEntry = {
|
export const defaultPaymentReceiveEntry = {
|
||||||
@@ -213,3 +216,47 @@ export const transformErrors = (errors, { setFieldError }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the payment receive totals.
|
||||||
|
*/
|
||||||
|
export const usePaymentReceiveTotals = () => {
|
||||||
|
const {
|
||||||
|
values: { entries, currency_code: currencyCode },
|
||||||
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Retrieves the invoice entries total.
|
||||||
|
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
|
|
||||||
|
// Retrieves the formatted total money.
|
||||||
|
const formattedTotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted subtotal.
|
||||||
|
const formattedSubtotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode, { money: false }),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
formattedTotal,
|
||||||
|
formattedSubtotal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the payment has foreign customer.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useEstimateIsForeignCustomer = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignCustomer = React.useMemo(
|
||||||
|
() => values.currency_code !== currentOrganization.base_currency,
|
||||||
|
[values.currency_code, currentOrganization.base_currency],
|
||||||
|
);
|
||||||
|
return isForeignCustomer;
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,29 +8,37 @@ import {
|
|||||||
TotalLineBorderStyle,
|
TotalLineBorderStyle,
|
||||||
TotalLineTextStyle,
|
TotalLineTextStyle,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { useReceiptTotals } from './utils';
|
||||||
|
|
||||||
export function ReceiptFormFooterRight() {
|
export function ReceiptFormFooterRight() {
|
||||||
|
const {
|
||||||
|
formattedSubtotal,
|
||||||
|
formattedTotal,
|
||||||
|
formattedDueTotal,
|
||||||
|
formattedPaymentTotal,
|
||||||
|
} = useReceiptTotals();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReceiptTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
<ReceiptTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'receipt.details.subtotal'} />}
|
title={<T id={'receipt.details.subtotal'} />}
|
||||||
value={'$5000.00'}
|
value={formattedSubtotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'receipt.details.total'} />}
|
title={<T id={'receipt.details.total'} />}
|
||||||
value={'$5000.00'}
|
value={formattedTotal}
|
||||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'receipt.details.payment_amount'} />}
|
title={<T id={'receipt.details.payment_amount'} />}
|
||||||
value={'$0.00'}
|
value={formattedPaymentTotal}
|
||||||
borderStyle={TotalLineBorderStyle.None}
|
borderStyle={TotalLineBorderStyle.None}
|
||||||
/>
|
/>
|
||||||
<TotalLine
|
<TotalLine
|
||||||
title={<T id={'receipt.details.due_amount'} />}
|
title={<T id={'receipt.details.due_amount'} />}
|
||||||
value={'$5000.00'}
|
value={formattedDueTotal}
|
||||||
textStyle={TotalLineTextStyle.Bold}
|
textStyle={TotalLineTextStyle.Bold}
|
||||||
/>
|
/>
|
||||||
</ReceiptTotalLines>
|
</ReceiptTotalLines>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
If,
|
If,
|
||||||
InputPrependButton,
|
InputPrependButton,
|
||||||
ExchangeRateInputGroup,
|
CustomerDrawerLink,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
@@ -31,13 +31,13 @@ import {
|
|||||||
handleDateChange,
|
handleDateChange,
|
||||||
inputIntent,
|
inputIntent,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import ReceiptFormCurrencyTag from './ReceiptFormCurrencyTag';
|
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
import {
|
import {
|
||||||
accountsFieldShouldUpdate,
|
accountsFieldShouldUpdate,
|
||||||
customersFieldShouldUpdate,
|
customersFieldShouldUpdate,
|
||||||
useObserveReceiptNoSettings,
|
useObserveReceiptNoSettings,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
import { ReceiptExchangeRateInputField } from './components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt form header fields.
|
* Receipt form header fields.
|
||||||
@@ -46,22 +46,12 @@ function ReceiptFormHeader({
|
|||||||
//#withDialogActions
|
//#withDialogActions
|
||||||
openDialog,
|
openDialog,
|
||||||
|
|
||||||
// #ownProps
|
|
||||||
onReceiptNumberChanged,
|
|
||||||
|
|
||||||
// #withSettings
|
// #withSettings
|
||||||
receiptAutoIncrement,
|
receiptAutoIncrement,
|
||||||
receiptNextNumber,
|
receiptNextNumber,
|
||||||
receiptNumberPrefix,
|
receiptNumberPrefix,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const { accounts, customers } = useReceiptFormContext();
|
||||||
accounts,
|
|
||||||
customers,
|
|
||||||
isForeignCustomer,
|
|
||||||
baseCurrency,
|
|
||||||
selectCustomer,
|
|
||||||
setSelectCustomer,
|
|
||||||
} = useReceiptFormContext();
|
|
||||||
|
|
||||||
const handleReceiptNumberChange = useCallback(() => {
|
const handleReceiptNumberChange = useCallback(() => {
|
||||||
openDialog('receipt-number-form', {});
|
openDialog('receipt-number-form', {});
|
||||||
@@ -108,26 +98,25 @@ function ReceiptFormHeader({
|
|||||||
onContactSelected={(customer) => {
|
onContactSelected={(customer) => {
|
||||||
form.setFieldValue('customer_id', customer.id);
|
form.setFieldValue('customer_id', customer.id);
|
||||||
form.setFieldValue('currency_code', customer?.currency_code);
|
form.setFieldValue('currency_code', customer?.currency_code);
|
||||||
setSelectCustomer(customer);
|
|
||||||
}}
|
}}
|
||||||
popoverFill={true}
|
popoverFill={true}
|
||||||
allowCreate={true}
|
allowCreate={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</ControlCustomerGroup>
|
</ControlCustomerGroup>
|
||||||
|
{value && (
|
||||||
|
<CustomerButtonLink customerId={value}>
|
||||||
|
View Customer Details
|
||||||
|
</CustomerButtonLink>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<If condition={isForeignCustomer}>
|
<ReceiptExchangeRateInputField
|
||||||
<ExchangeRateInputGroup
|
|
||||||
fromCurrency={baseCurrency}
|
|
||||||
toCurrency={selectCustomer?.currency_code}
|
|
||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
{/* ----------- Deposit account ----------- */}
|
{/* ----------- Deposit account ----------- */}
|
||||||
<FastField
|
<FastField
|
||||||
@@ -261,3 +250,8 @@ const ControlCustomerGroup = styled(ControlGroup)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transform: none;
|
transform: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const CustomerButtonLink = styled(CustomerDrawerLink)`
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|||||||
29
src/containers/Sales/Receipts/ReceiptForm/components.js
Normal file
29
src/containers/Sales/Receipts/ReceiptForm/components.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import { ExchangeRateInputGroup } from 'components';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { useReceiptIsForeignCustomer } from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receipt exchange rate input field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function ReceiptExchangeRateInputField({ ...props }) {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
const isForeignCustomer = useReceiptIsForeignCustomer();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignCustomer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,12 +9,15 @@ import {
|
|||||||
transactionNumber,
|
transactionNumber,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
transformToForm,
|
transformToForm,
|
||||||
|
formattedAmount,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
import {
|
import {
|
||||||
updateItemsEntriesTotal,
|
updateItemsEntriesTotal,
|
||||||
ensureEntriesHaveEmptyLine,
|
ensureEntriesHaveEmptyLine,
|
||||||
} from 'containers/Entries/utils';
|
} from 'containers/Entries/utils';
|
||||||
|
import { useCurrentOrganization } from 'hooks/state';
|
||||||
|
import { getEntriesTotal } from 'containers/Entries/utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
|
|
||||||
@@ -178,3 +181,69 @@ export const useSetPrimaryBranchToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the Receipt totals.
|
||||||
|
*/
|
||||||
|
export const useReceiptTotals = () => {
|
||||||
|
const {
|
||||||
|
values: { entries, currency_code: currencyCode },
|
||||||
|
} = useFormikContext();
|
||||||
|
|
||||||
|
// Retrieves the invoice entries total.
|
||||||
|
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||||
|
|
||||||
|
// Retrieves the formatted total money.
|
||||||
|
const formattedTotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted subtotal.
|
||||||
|
const formattedSubtotal = React.useMemo(
|
||||||
|
() => formattedAmount(total, currencyCode, { money: false }),
|
||||||
|
[total, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the payment total.
|
||||||
|
const paymentTotal = React.useMemo(() => 0, []);
|
||||||
|
|
||||||
|
// Retireves the formatted payment total.
|
||||||
|
const formattedPaymentTotal = React.useMemo(
|
||||||
|
() => formattedAmount(paymentTotal, currencyCode),
|
||||||
|
[paymentTotal, currencyCode],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted due total.
|
||||||
|
const dueTotal = React.useMemo(
|
||||||
|
() => total - paymentTotal,
|
||||||
|
[total, paymentTotal],
|
||||||
|
);
|
||||||
|
// Retrieves the formatted due total.
|
||||||
|
const formattedDueTotal = React.useMemo(
|
||||||
|
() => formattedAmount(dueTotal, currencyCode),
|
||||||
|
[dueTotal, currencyCode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
paymentTotal,
|
||||||
|
dueTotal,
|
||||||
|
formattedTotal,
|
||||||
|
formattedSubtotal,
|
||||||
|
formattedPaymentTotal,
|
||||||
|
formattedDueTotal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the receipt has foreign customer.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useReceiptIsForeignCustomer = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignCustomer = React.useMemo(
|
||||||
|
() => values.currency_code !== currentOrganization.base_currency,
|
||||||
|
[values.currency_code, currentOrganization.base_currency],
|
||||||
|
);
|
||||||
|
return isForeignCustomer;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user