diff --git a/src/components/Vendors/VendorDrawerLink.js b/src/components/Vendors/VendorDrawerLink.js index 91b528e6f..9fdabf6df 100644 --- a/src/components/Vendors/VendorDrawerLink.js +++ b/src/components/Vendors/VendorDrawerLink.js @@ -13,8 +13,9 @@ function VendorDrawerLinkComponent({ openDrawer, }) { // Handle view customer drawer. - const handleVendorDrawer = () => { + const handleVendorDrawer = (event) => { openDrawer('vendor-details-drawer', { vendorId }); + event.preventDefault(); }; return {children}; diff --git a/src/containers/Purchases/Bills/BillForm/BillFormFooterRight.js b/src/containers/Purchases/Bills/BillForm/BillFormFooterRight.js index f5f5b385e..05ae8719f 100644 --- a/src/containers/Purchases/Bills/BillForm/BillFormFooterRight.js +++ b/src/containers/Purchases/Bills/BillForm/BillFormFooterRight.js @@ -8,29 +8,37 @@ import { TotalLineBorderStyle, TotalLineTextStyle, } from 'components'; +import { useBillTotals } from './utils'; export function BillFormFooterRight() { + const { + formattedSubtotal, + formattedTotal, + formattedDueTotal, + formattedPaymentTotal, + } = useBillTotals(); + return ( } - value={'$5000.00'} + value={formattedSubtotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedTotal} borderStyle={TotalLineBorderStyle.SingleDark} textStyle={TotalLineTextStyle.Bold} /> } - value={'$0.00'} + value={formattedPaymentTotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedDueTotal} textStyle={TotalLineTextStyle.Bold} /> diff --git a/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js b/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js index 8844d014a..335189720 100644 --- a/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js +++ b/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js @@ -16,13 +16,13 @@ import { VendorSelectField, FieldRequiredHint, Icon, - ExchangeRateInputGroup, - If, + CustomerDrawerLink, + VendorDrawerLink, } from 'components'; import { vendorsFieldShouldUpdate } from './utils'; import { useBillFormContext } from './BillFormProvider'; -import BillFormCurrencyTag from './BillFormCurrencyTag'; +import { BillExchangeRateInputField } from './components'; import withDialogActions from 'containers/Dialog/withDialogActions'; import { momentFormatter, @@ -37,13 +37,7 @@ import { */ function BillFormHeader() { // Bill form context. - const { - vendors, - isForeignVendor, - baseCurrency, - selectVendor, - setSelectVendor, - } = useBillFormContext(); + const { vendors } = useBillFormContext(); return (
@@ -57,7 +51,11 @@ function BillFormHeader() { } inline={true} - className={classNames(CLASSES.FILL, 'form-group--vendor')} + className={classNames( + 'form-group--customer-name', + 'form-group--select-list', + CLASSES.FILL, + )} labelInfo={} intent={inputIntent({ error, touched })} helperText={} @@ -70,26 +68,25 @@ function BillFormHeader() { onContactSelected={(contact) => { form.setFieldValue('vendor_id', contact.id); form.setFieldValue('currency_code', contact?.currency_code); - setSelectVendor(contact); }} popoverFill={true} allowCreate={true} /> - + {value && ( + + View Vendor Details + + )} )} {/* ----------- Exchange rate ----------- */} - - - + {/* ------- Bill date ------- */} @@ -184,3 +181,8 @@ const ControlVendorGroup = styled(ControlGroup)` align-items: center; transform: none; `; + +const VendorButtonLink = styled(VendorDrawerLink)` + font-size: 11px; + margin-top: 6px; +`; diff --git a/src/containers/Purchases/Bills/BillForm/components.js b/src/containers/Purchases/Bills/BillForm/components.js new file mode 100644 index 000000000..842a7e9f0 --- /dev/null +++ b/src/containers/Purchases/Bills/BillForm/components.js @@ -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 ( + + ); +} diff --git a/src/containers/Purchases/Bills/BillForm/utils.js b/src/containers/Purchases/Bills/BillForm/utils.js index 33e856c01..a88ce2a05 100644 --- a/src/containers/Purchases/Bills/BillForm/utils.js +++ b/src/containers/Purchases/Bills/BillForm/utils.js @@ -11,12 +11,14 @@ import { transformToForm, repeatValue, orderingLinesIndexes, + formattedAmount, } from 'utils'; import { updateItemsEntriesTotal, ensureEntriesHaveEmptyLine, } from 'containers/Entries/utils'; -import { isLandedCostDisabled } from '../../../Entries/utils'; +import { useCurrentOrganization } from 'hooks/state'; +import { isLandedCostDisabled, getEntriesTotal } from '../../../Entries/utils'; import { useBillFormContext } from './BillFormProvider'; export const MIN_LINES_NUMBER = 1; @@ -216,3 +218,69 @@ export const useSetPrimaryWarehouseToForm = () => { } }, [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; +}; diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormFooterRight.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormFooterRight.js index 16c702767..941350ba0 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormFooterRight.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormFooterRight.js @@ -7,18 +7,21 @@ import { TotalLineBorderStyle, TotalLineTextStyle, } from 'components'; +import { useCreditNoteTotals } from './utils'; export function CreditNoteFormFooterRight() { + const { formattedSubtotal, formattedTotal } = useCreditNoteTotals(); + return ( } - value={'$5000.00'} + value={formattedSubtotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedTotal} // borderStyle={TotalLineBorderStyle.SingleDark} textStyle={TotalLineTextStyle.Bold} /> diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js index ee8a9f086..f06fcce36 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js @@ -15,9 +15,8 @@ import { FieldRequiredHint, InputPrependButton, Icon, - If, FormattedMessage as T, - ExchangeRateInputGroup, + CustomerDrawerLink, } from 'components'; import { customerNameFieldShouldUpdate, @@ -27,7 +26,7 @@ import { import { useCreditNoteFormContext } from './CreditNoteFormProvider'; import withSettings from 'containers/Settings/withSettings'; import withDialogActions from 'containers/Dialog/withDialogActions'; -import CreditNotetFormCurrencyTag from './CreditNotetFormCurrencyTag'; +import { CreditNoteExchangeRateInputField } from './components'; import { momentFormatter, compose, @@ -49,13 +48,7 @@ function CreditNoteFormHeaderFields({ creditNextNumber, }) { // Credit note form context. - const { - customers, - isForeignCustomer, - baseCurrency, - selectCustomer, - setSelectCustomer, - } = useCreditNoteFormContext(); + const { customers } = useCreditNoteFormContext(); // Handle credit number changing. const handleCreditNumberChange = () => { @@ -108,25 +101,25 @@ function CreditNoteFormHeaderFields({ onContactSelected={(customer) => { form.setFieldValue('customer_id', customer.id); form.setFieldValue('currency_code', customer?.currency_code); - setSelectCustomer(customer); }} popoverFill={true} allowCreate={true} /> + {value && ( + + View Customer Details + + )} )} {/* ----------- Exchange rate ----------- */} - - - + {/* ----------- Credit note date ----------- */} @@ -222,3 +215,7 @@ const ControlCustomerGroup = styled(ControlGroup)` align-items: center; transform: none; `; +const CustomerButtonLink = styled(CustomerDrawerLink)` + font-size: 11px; + margin-top: 6px; +`; diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/components.js b/src/containers/Sales/CreditNotes/CreditNoteForm/components.js new file mode 100644 index 000000000..466d5a802 --- /dev/null +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/components.js @@ -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 ( + + ); +} \ No newline at end of file diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js b/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js index d8a26fad0..96c2d7add 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js @@ -8,6 +8,7 @@ import { transformToForm, repeatValue, transactionNumber, + formattedAmount, orderingLinesIndexes, } from 'utils'; import { useFormikContext } from 'formik'; @@ -17,6 +18,8 @@ import { updateItemsEntriesTotal, ensureEntriesHaveEmptyLine, } from 'containers/Entries/utils'; +import { useCurrentOrganization } from 'hooks/state'; +import { getEntriesTotal } from 'containers/Entries/utils'; export const MIN_LINES_NUMBER = 1; @@ -168,3 +171,47 @@ export const useSetPrimaryWarehouseToForm = () => { } }, [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; +}; diff --git a/src/containers/Sales/Estimates/EstimateForm/EstimateFormFooterRight.js b/src/containers/Sales/Estimates/EstimateForm/EstimateFormFooterRight.js index 38b76c3b8..107fb2b66 100644 --- a/src/containers/Sales/Estimates/EstimateForm/EstimateFormFooterRight.js +++ b/src/containers/Sales/Estimates/EstimateForm/EstimateFormFooterRight.js @@ -7,18 +7,22 @@ import { TotalLineBorderStyle, TotalLineTextStyle, } from 'components'; +import { useEstimateTotals } from './utils'; export function EstimateFormFooterRight() { + + const { formattedSubtotal, formattedTotal } = useEstimateTotals(); + return ( } - value={'$5000.00'} + value={formattedSubtotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedTotal} // borderStyle={TotalLineBorderStyle.SingleDark} textStyle={TotalLineTextStyle.Bold} /> diff --git a/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeaderFields.js b/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeaderFields.js index 7a84fc318..34d4db271 100644 --- a/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeaderFields.js +++ b/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeaderFields.js @@ -23,16 +23,15 @@ import { CLASSES } from 'common/classes'; import { CustomerSelectField, FieldRequiredHint, - If, Icon, InputPrependButton, - ExchangeRateInputGroup, + CustomerDrawerLink, } from 'components'; import withDialogActions from 'containers/Dialog/withDialogActions'; import withSettings from 'containers/Settings/withSettings'; -import EstimateFormCurrencyTag from './EstimateFormCurrencyTag'; import { useObserveEstimateNoSettings } from './utils'; +import { EstimateExchangeRateInputField } from './components'; import { useEstimateFormContext } from './EstimateFormProvider'; /** @@ -47,13 +46,7 @@ function EstimateFormHeader({ estimateNumberPrefix, estimateNextNumber, }) { - const { - customers, - isForeignCustomer, - baseCurrency, - selectCustomer, - setSelectCustomer, - } = useEstimateFormContext(); + const { customers } = useEstimateFormContext(); const handleEstimateNumberBtnClick = () => { openDialog('estimate-number-form', {}); @@ -100,27 +93,26 @@ function EstimateFormHeader({ onContactSelected={(customer) => { form.setFieldValue('customer_id', customer.id); form.setFieldValue('currency_code', customer?.currency_code); - setSelectCustomer(customer); }} popoverFill={true} intent={inputIntent({ error, touched })} allowCreate={true} /> - + {value && ( + + View Customer Details + + )} )} {/* ----------- Exchange rate ----------- */} - - - + {/* ----------- Estimate date ----------- */} @@ -246,3 +238,8 @@ const ControlCustomerGroup = styled(ControlGroup)` align-items: center; transform: none; `; + +const CustomerButtonLink = styled(CustomerDrawerLink)` + font-size: 11px; + margin-top: 6px; +`; diff --git a/src/containers/Sales/Estimates/EstimateForm/components.js b/src/containers/Sales/Estimates/EstimateForm/components.js new file mode 100644 index 000000000..8a4ada2f6 --- /dev/null +++ b/src/containers/Sales/Estimates/EstimateForm/components.js @@ -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 ( + + ); +} diff --git a/src/containers/Sales/Estimates/EstimateForm/utils.js b/src/containers/Sales/Estimates/EstimateForm/utils.js index 7de4acd68..0fb87d970 100644 --- a/src/containers/Sales/Estimates/EstimateForm/utils.js +++ b/src/containers/Sales/Estimates/EstimateForm/utils.js @@ -9,12 +9,15 @@ import { transactionNumber, repeatValue, transformToForm, + formattedAmount, } from 'utils'; import { useEstimateFormContext } from './EstimateFormProvider'; import { updateItemsEntriesTotal, ensureEntriesHaveEmptyLine, } from 'containers/Entries/utils'; +import { useCurrentOrganization } from 'hooks/state'; +import { getEntriesTotal } from 'containers/Entries/utils'; export const MIN_LINES_NUMBER = 1; @@ -187,3 +190,47 @@ export const useSetPrimaryBranchToForm = () => { } }, [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; +}; diff --git a/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js b/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js index 0019fdce2..ca9c58d5e 100644 --- a/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js +++ b/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js @@ -104,7 +104,6 @@ function InvoiceFormHeaderFields({ popoverFill={true} allowCreate={true} /> - {/* */} {value && ( diff --git a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetLeft.js b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetLeft.js index 470bcea50..795e628a3 100644 --- a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetLeft.js +++ b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetLeft.js @@ -6,7 +6,7 @@ export function PaymentReceiveFormFootetLeft() { return ( {/* --------- Statement--------- */} - } hintText={'Will be displayed on the Payment'} @@ -15,15 +15,13 @@ export function PaymentReceiveFormFootetLeft() { name={'statement'} placeholder={'Thanks for your business and have a great day!'} /> - + ); } -const StatementFormGroup = styled(FFormGroup)` +const TermsConditsFormGroup = styled(FFormGroup)` &.bp3-form-group { - margin-bottom: 40px; - .bp3-label { font-size: 12px; margin-bottom: 12px; @@ -33,3 +31,4 @@ const StatementFormGroup = styled(FFormGroup)` } } `; + diff --git a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetRight.js b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetRight.js index d185510dd..3734dc753 100644 --- a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetRight.js +++ b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormFootetRight.js @@ -7,18 +7,21 @@ import { TotalLineBorderStyle, TotalLineTextStyle, } from 'components'; +import { usePaymentReceiveTotals } from './utils'; export function PaymentReceiveFormFootetRight() { + const { formattedSubtotal, formattedTotal } = usePaymentReceiveTotals(); + return ( } - value={'$5000.00'} + value={formattedSubtotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedTotal} // borderStyle={TotalLineBorderStyle.SingleDark} textStyle={TotalLineTextStyle.Bold} /> diff --git a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js index 50200edcd..bb7616c8a 100644 --- a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js +++ b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js @@ -30,13 +30,13 @@ import { InputPrependButton, MoneyInputGroup, InputPrependText, - ExchangeRateInputGroup, + CustomerDrawerLink, Hint, Money, } from 'components'; import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider'; import { ACCOUNT_TYPE } from 'common/accountTypes'; -import PaymentReceiveFormCurrencyTag from './PaymentReceiveFormCurrencyTag'; +import { PaymentReceiveExchangeRateInputField } from './components'; import withDialogActions from 'containers/Dialog/withDialogActions'; import withSettings from 'containers/Settings/withSettings'; @@ -49,6 +49,7 @@ import { customersFieldShouldUpdate, accountsFieldShouldUpdate, } from './utils'; + import { toSafeInteger } from 'lodash'; /** @@ -67,15 +68,7 @@ function PaymentReceiveHeaderFields({ paymentReceiveNextNumber, }) { // Payment receive form context. - const { - customers, - accounts, - isNewMode, - isForeignCustomer, - baseCurrency, - selectCustomer, - setSelectCustomer, - } = usePaymentReceiveFormContext(); + const { customers, accounts, isNewMode } = usePaymentReceiveFormContext(); // Formik form context. const { @@ -154,7 +147,6 @@ function PaymentReceiveHeaderFields({ form.setFieldValue('customer_id', customer.id); form.setFieldValue('full_amount', ''); form.setFieldValue('currency_code', customer?.currency_code); - setSelectCustomer(customer); }} popoverFill={true} disabled={!isNewMode} @@ -164,20 +156,20 @@ function PaymentReceiveHeaderFields({ allowCreate={true} /> + {value && ( + + View Customer Details + + )} )} {/* ----------- Exchange rate ----------- */} - - - - + {/* ------------- Payment date ------------- */} {({ form, field: { value }, meta: { error, touched } }) => ( @@ -359,3 +351,8 @@ const ControlCustomerGroup = styled(ControlGroup)` align-items: center; transform: none; `; + +const CustomerButtonLink = styled(CustomerDrawerLink)` + font-size: 11px; + margin-top: 6px; +`; diff --git a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js index 8941d6467..4f17607e6 100644 --- a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js +++ b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js @@ -2,9 +2,12 @@ import React from 'react'; import moment from 'moment'; import intl from 'react-intl-universal'; -import { Money } from 'components'; +import { Money, ExchangeRateInputGroup } from 'components'; import { MoneyFieldCell } from 'components/DataTableCells'; import { safeSumBy, formattedAmount } from 'utils'; +import { useFormikContext } from 'formik'; +import { useCurrentOrganization } from 'hooks/state'; +import { useEstimateIsForeignCustomer } from './utils'; /** * Invoice date cell. @@ -59,15 +62,13 @@ function MoneyTableCell({ row: { original }, value }) { } function DateFooterCell() { - return intl.get('total') + return intl.get('total'); } /** * Retrieve payment receive form entries columns. */ export const usePaymentReceiveEntriesColumns = () => { - - return React.useMemo( () => [ { @@ -127,3 +128,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 ( + + ); +} diff --git a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js index ada3570e8..e21d6cc12 100644 --- a/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js +++ b/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js @@ -12,7 +12,10 @@ import { transformToForm, safeSumBy, orderingLinesIndexes, + formattedAmount, } from 'utils'; +import { useCurrentOrganization } from 'hooks/state'; +import { getEntriesTotal } from 'containers/Entries/utils'; // Default payment receive entry. 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; +}; diff --git a/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormFooterRight.js b/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormFooterRight.js index a297162ae..ab050893f 100644 --- a/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormFooterRight.js +++ b/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormFooterRight.js @@ -8,29 +8,37 @@ import { TotalLineBorderStyle, TotalLineTextStyle, } from 'components'; +import { useReceiptTotals } from './utils'; export function ReceiptFormFooterRight() { + const { + formattedSubtotal, + formattedTotal, + formattedDueTotal, + formattedPaymentTotal, + } = useReceiptTotals(); + return ( } - value={'$5000.00'} + value={formattedSubtotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedTotal} borderStyle={TotalLineBorderStyle.SingleDark} textStyle={TotalLineTextStyle.Bold} /> } - value={'$0.00'} + value={formattedPaymentTotal} borderStyle={TotalLineBorderStyle.None} /> } - value={'$5000.00'} + value={formattedDueTotal} textStyle={TotalLineTextStyle.Bold} /> diff --git a/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js b/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js index 3bf129059..03f812dd1 100644 --- a/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js +++ b/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js @@ -19,7 +19,7 @@ import { Icon, If, InputPrependButton, - ExchangeRateInputGroup, + CustomerDrawerLink, } from 'components'; import withSettings from 'containers/Settings/withSettings'; import withDialogActions from 'containers/Dialog/withDialogActions'; @@ -31,13 +31,13 @@ import { handleDateChange, inputIntent, } from 'utils'; -import ReceiptFormCurrencyTag from './ReceiptFormCurrencyTag'; import { useReceiptFormContext } from './ReceiptFormProvider'; import { accountsFieldShouldUpdate, customersFieldShouldUpdate, useObserveReceiptNoSettings, } from './utils'; +import { ReceiptExchangeRateInputField } from './components'; /** * Receipt form header fields. @@ -46,22 +46,12 @@ function ReceiptFormHeader({ //#withDialogActions openDialog, - // #ownProps - onReceiptNumberChanged, - // #withSettings receiptAutoIncrement, receiptNextNumber, receiptNumberPrefix, }) { - const { - accounts, - customers, - isForeignCustomer, - baseCurrency, - selectCustomer, - setSelectCustomer, - } = useReceiptFormContext(); + const { accounts, customers } = useReceiptFormContext(); const handleReceiptNumberChange = useCallback(() => { openDialog('receipt-number-form', {}); @@ -108,26 +98,25 @@ function ReceiptFormHeader({ onContactSelected={(customer) => { form.setFieldValue('customer_id', customer.id); form.setFieldValue('currency_code', customer?.currency_code); - setSelectCustomer(customer); }} popoverFill={true} allowCreate={true} /> - + {value && ( + + View Customer Details + + )} )} {/* ----------- Exchange rate ----------- */} - - - + {/* ----------- Deposit account ----------- */} + ); +} \ No newline at end of file diff --git a/src/containers/Sales/Receipts/ReceiptForm/utils.js b/src/containers/Sales/Receipts/ReceiptForm/utils.js index 64378259c..cc69f4244 100644 --- a/src/containers/Sales/Receipts/ReceiptForm/utils.js +++ b/src/containers/Sales/Receipts/ReceiptForm/utils.js @@ -9,12 +9,15 @@ import { transactionNumber, repeatValue, transformToForm, + formattedAmount, } from 'utils'; import { useReceiptFormContext } from './ReceiptFormProvider'; import { updateItemsEntriesTotal, ensureEntriesHaveEmptyLine, } from 'containers/Entries/utils'; +import { useCurrentOrganization } from 'hooks/state'; +import { getEntriesTotal } from 'containers/Entries/utils'; export const MIN_LINES_NUMBER = 1; @@ -178,3 +181,69 @@ export const useSetPrimaryBranchToForm = () => { } }, [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; +};