mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
fix: FastField re-rendering.
fix: Allocate landed cost dialog.
This commit is contained in:
5
client/src/components/Card.js
Normal file
5
client/src/components/Card.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Card({ children }) {
|
||||
return <div class="card">{children}</div>;
|
||||
}
|
||||
@@ -4,14 +4,26 @@ import { CLASSES } from 'common/classes';
|
||||
import { DataTable, If } from 'components';
|
||||
import 'style/components/DataTable/DataTableEditable.scss';
|
||||
|
||||
/**
|
||||
* Editable datatable.
|
||||
*/
|
||||
export default function DatatableEditable({
|
||||
totalRow = false,
|
||||
actions,
|
||||
name,
|
||||
className,
|
||||
...tableProps
|
||||
}) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.DATATABLE_EDITOR, className)}>
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.DATATABLE_EDITOR,
|
||||
{
|
||||
[`${CLASSES.DATATABLE_EDITOR}--${name}`]: name,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<DataTable {...tableProps} />
|
||||
|
||||
<If condition={actions}>
|
||||
|
||||
29
client/src/components/Details/index.js
Normal file
29
client/src/components/Details/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import className from 'classname';
|
||||
|
||||
/**
|
||||
* Details menu.
|
||||
*/
|
||||
export function DetailsMenu({ children, vertical = false }) {
|
||||
return (
|
||||
<div
|
||||
className={className('details-menu', {
|
||||
'is-vertical': vertical,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail item.
|
||||
*/
|
||||
export function DetailItem({ label, children }) {
|
||||
return (
|
||||
<div class="detail-item">
|
||||
<div class="detail-item__label">{label}</div>
|
||||
<div class="detail-item__content">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Position, Drawer } from '@blueprintjs/core';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import 'style/components/Drawer.scss';
|
||||
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Drawer component.
|
||||
*/
|
||||
function DrawerComponent(props) {
|
||||
const { name, children, onClose, closeDrawer } = props;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ import Postbox from './Postbox';
|
||||
import AccountsSuggestField from './AccountsSuggestField';
|
||||
import MaterialProgressBar from './MaterialProgressBar';
|
||||
import { MoneyFieldCell } from './DataTableCells';
|
||||
import Card from './Card';
|
||||
|
||||
import { ItemsMultiSelect } from './Items';
|
||||
|
||||
@@ -127,5 +128,6 @@ export {
|
||||
AccountsSuggestField,
|
||||
MaterialProgressBar,
|
||||
MoneyFieldCell,
|
||||
ItemsMultiSelect
|
||||
ItemsMultiSelect,
|
||||
Card
|
||||
};
|
||||
|
||||
@@ -67,4 +67,4 @@ export const useManualJournalsColumns = () => {
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -3,16 +3,28 @@ import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
|
||||
import { defaultEntry, MIN_LINES_NUMBER } from './utils';
|
||||
import { entriesFieldShouldUpdate, defaultEntry, MIN_LINES_NUMBER } from './utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
|
||||
/**
|
||||
* Make journal entries field.
|
||||
*/
|
||||
export default function MakeJournalEntriesField() {
|
||||
const { accounts, contacts } = useMakeJournalFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
{({ form:{values ,setFieldValue}, field: { value }, meta: { error, touched } }) => (
|
||||
<FastField
|
||||
name={'entries'}
|
||||
contacts={contacts}
|
||||
accounts={accounts}
|
||||
shouldUpdate={entriesFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<MakeJournalEntriesTable
|
||||
onChange={(entries) => {
|
||||
setFieldValue('entries', entries);
|
||||
|
||||
@@ -29,7 +29,10 @@ import {
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { useObserveJournalNoSettings } from './utils';
|
||||
import {
|
||||
currenciesFieldShouldUpdate,
|
||||
useObserveJournalNoSettings,
|
||||
} from './utils';
|
||||
/**
|
||||
* Make journal entries header.
|
||||
*/
|
||||
@@ -182,7 +185,11 @@ function MakeJournalEntriesHeader({
|
||||
</FastField>
|
||||
|
||||
{/*------------ Currency -----------*/}
|
||||
<FastField name={'currency_code'}>
|
||||
<FastField
|
||||
name={'currency_code'}
|
||||
currencies={currencies}
|
||||
shouldUpdate={currenciesFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'currency'} />}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { sumBy, setWith, toSafeInteger, get, values } from 'lodash';
|
||||
import { sumBy, setWith, toSafeInteger, get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import {
|
||||
transactionNumber,
|
||||
updateTableRow,
|
||||
repeatValue,
|
||||
transformToForm,
|
||||
defaultFastFieldShouldUpdate,
|
||||
} from 'utils';
|
||||
import { AppToaster } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
@@ -123,17 +123,17 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
|
||||
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
||||
}
|
||||
if ((error = getError(ERROR.ENTRIES_SHOULD_ASSIGN_WITH_CONTACT))) {
|
||||
if (error.meta.find(meta => meta.contact_type === 'customer')) {
|
||||
if (error.meta.find((meta) => meta.contact_type === 'customer')) {
|
||||
toastMessages.push(
|
||||
intl.get('receivable_accounts_should_assign_with_customers'),
|
||||
);
|
||||
}
|
||||
if (error.meta.find(meta => meta.contact_type === 'vendor')) {
|
||||
if (error.meta.find((meta) => meta.contact_type === 'vendor')) {
|
||||
toastMessages.push(
|
||||
intl.get('payable_accounts_should_assign_with_vendors'),
|
||||
);
|
||||
}
|
||||
const indexes = error.meta.map((meta => meta.indexes)).flat();
|
||||
const indexes = error.meta.map((meta) => meta.indexes).flat();
|
||||
setEntriesErrors(indexes, 'contact_id', 'error');
|
||||
}
|
||||
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
||||
@@ -159,7 +159,28 @@ export const useObserveJournalNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
const journalNo = transactionNumber(prefix, nextNumber);
|
||||
const journalNo = transactionNumber(prefix, nextNumber);
|
||||
setFieldValue('journal_number', journalNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines entries fast field should update.
|
||||
*/
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.contacts !== oldProps.contacts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines currencies fast field should update.
|
||||
*/
|
||||
export const currenciesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.currencies !== oldProps.currencies ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ function BillTransactionDeleteAlert({
|
||||
onConfirm={handleConfirmLandedCostDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>{/* <T id={''}/> */}</p>
|
||||
<p><T id={`Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?`}/></p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import moment from 'moment';
|
||||
import { sumBy } from 'lodash';
|
||||
|
||||
import 'style/pages/AllocateLandedCost/AllocateLandedCostForm.scss';
|
||||
|
||||
@@ -48,6 +49,7 @@ function AllocateLandedCostForm({
|
||||
cost: '',
|
||||
})),
|
||||
};
|
||||
const amount = sumBy(initialValues.items, 'amount');
|
||||
|
||||
// Handle form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting }) => {
|
||||
@@ -84,9 +86,12 @@ function AllocateLandedCostForm({
|
||||
createLandedCostMutate([billId, form]).then(onSuccess).catch(onError);
|
||||
};
|
||||
|
||||
// Computed validation schema.
|
||||
const validationSchema = AllocateLandedCostFormSchema(amount);
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={AllocateLandedCostFormSchema}
|
||||
validationSchema={validationSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={AllocateLandedCostFormContent}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import * as Yup from 'yup';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
transaction_type: Yup.string().label(intl.get('transaction_type')),
|
||||
transaction_date: Yup.date().label(intl.get('transaction_date')),
|
||||
transaction_id: Yup.string().label(intl.get('transaction_number')),
|
||||
transaction_entry_id: Yup.string().label(intl.get('transaction_line')),
|
||||
amount: Yup.number().label(intl.get('amount')),
|
||||
allocation_method: Yup.string().trim(),
|
||||
items: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
entry_id: Yup.number().nullable(),
|
||||
cost: Yup.number().nullable(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export const AllocateLandedCostFormSchema = Schema;
|
||||
export const AllocateLandedCostFormSchema = (minAmount) =>
|
||||
Yup.object().shape({
|
||||
transaction_type: Yup.string().label(intl.get('transaction_type')),
|
||||
transaction_date: Yup.date().label(intl.get('transaction_date')),
|
||||
transaction_id: Yup.string().label(intl.get('transaction_number')),
|
||||
transaction_entry_id: Yup.string().label(intl.get('transaction_line')),
|
||||
amount: Yup.number().max(minAmount).label(intl.get('amount')),
|
||||
allocation_method: Yup.string().trim(),
|
||||
items: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
entry_id: Yup.number().nullable(),
|
||||
cost: Yup.number().nullable(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import allocateLandedCostType from 'common/allocateLandedCostType';
|
||||
import { useLandedCostTransaction } from 'hooks/query';
|
||||
|
||||
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
|
||||
import { getEntriesByTransactionId } from './utils';
|
||||
import { getEntriesByTransactionId, allocateCostToEntries } from './utils';
|
||||
|
||||
/**
|
||||
* Allocate landed cost form fields.
|
||||
@@ -30,10 +30,10 @@ export default function AllocateLandedCostFormFields() {
|
||||
} = useLandedCostTransaction(values.transaction_type);
|
||||
|
||||
// Retrieve entries of the given transaction id.
|
||||
const transactionEntries = React.useMemo(() => getEntriesByTransactionId(
|
||||
transactions,
|
||||
values.transaction_id,
|
||||
), [transactions, values.transaction_id]);
|
||||
const transactionEntries = React.useMemo(
|
||||
() => getEntriesByTransactionId(transactions, values.transaction_id),
|
||||
[transactions, values.transaction_id],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
@@ -56,6 +56,8 @@ export default function AllocateLandedCostFormFields() {
|
||||
items={allocateLandedCostType}
|
||||
onItemSelect={(type) => {
|
||||
setFieldValue('transaction_type', type.value);
|
||||
setFieldValue('transaction_id', '');
|
||||
setFieldValue('transaction_entry_id', '');
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
@@ -82,13 +84,14 @@ export default function AllocateLandedCostFormFields() {
|
||||
items={transactions}
|
||||
onItemSelect={({ id }) => {
|
||||
form.setFieldValue('transaction_id', id);
|
||||
form.setFieldValue('transaction_entry_id', '');
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'id'}
|
||||
textProp={'name'}
|
||||
labelProp={'id'}
|
||||
defaultText={intl.get('select_transaction')}
|
||||
defaultText={intl.get('Select transaction')}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -112,15 +115,21 @@ export default function AllocateLandedCostFormFields() {
|
||||
<ListSelect
|
||||
items={transactionEntries}
|
||||
onItemSelect={({ id, amount }) => {
|
||||
form.setFieldValue('amount', amount)
|
||||
const { items, allocation_method } = form.values;
|
||||
|
||||
form.setFieldValue('amount', amount);
|
||||
form.setFieldValue('transaction_entry_id', id);
|
||||
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, allocation_method, items),
|
||||
);
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'id'}
|
||||
textProp={'name'}
|
||||
labelProp={'id'}
|
||||
defaultText={intl.get('select_transaction')}
|
||||
defaultText={intl.get('Select transaction entry')}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -138,13 +147,24 @@ export default function AllocateLandedCostFormFields() {
|
||||
className={'form-group--amount'}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup {...field} />
|
||||
<InputGroup
|
||||
{...field}
|
||||
onBlur={(e) => {
|
||||
const amount = e.target.value;
|
||||
const { allocation_method, items } = form.values;
|
||||
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, allocation_method, items),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Allocation method -----------*/}
|
||||
<FastField name={'allocation_method'}>
|
||||
<Field name={'allocation_method'}>
|
||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||
<FormGroup
|
||||
medium={true}
|
||||
@@ -157,7 +177,13 @@ export default function AllocateLandedCostFormFields() {
|
||||
>
|
||||
<RadioGroup
|
||||
onChange={handleStringChange((_value) => {
|
||||
const { amount, items, allocation_method } = form.values;
|
||||
|
||||
form.setFieldValue('allocation_method', _value);
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, allocation_method, items),
|
||||
);
|
||||
})}
|
||||
selectedValue={value}
|
||||
inline={true}
|
||||
@@ -167,7 +193,7 @@ export default function AllocateLandedCostFormFields() {
|
||||
</RadioGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
|
||||
{/*------------ Allocate Landed cost Table -----------*/}
|
||||
<AllocateLandedCostFormBody />
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { sumBy, round } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
/**
|
||||
* Retrieve transaction entries of the given transaction id.
|
||||
*/
|
||||
@@ -5,3 +7,56 @@ export function getEntriesByTransactionId(transactions, id) {
|
||||
const transaction = transactions.find((trans) => trans.id === id);
|
||||
return transaction ? transaction.entries : [];
|
||||
}
|
||||
|
||||
export function allocateCostToEntries(total, allocateType, entries) {
|
||||
return R.compose(
|
||||
R.when(
|
||||
R.always(allocateType === 'value'),
|
||||
R.curry(allocateCostByValue)(total),
|
||||
),
|
||||
R.when(
|
||||
R.always(allocateType === 'quantity'),
|
||||
R.curry(allocateCostByQuantity)(total),
|
||||
),
|
||||
)(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate total cost on entries on value.
|
||||
* @param {*} entries
|
||||
* @param {*} total
|
||||
* @returns
|
||||
*/
|
||||
export function allocateCostByValue(total, entries) {
|
||||
const totalAmount = sumBy(entries, 'amount');
|
||||
|
||||
const _entries = entries.map((entry) => ({
|
||||
...entry,
|
||||
percentageOfValue: entry.amount / totalAmount,
|
||||
}));
|
||||
|
||||
return _entries.map((entry) => ({
|
||||
...entry,
|
||||
cost: round(entry.percentageOfValue * total, 2),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate total cost on entries by quantity.
|
||||
* @param {*} entries
|
||||
* @param {*} total
|
||||
* @returns
|
||||
*/
|
||||
export function allocateCostByQuantity(total, entries) {
|
||||
const totalQuantity = sumBy(entries, 'quantity');
|
||||
|
||||
const _entries = entries.map((entry) => ({
|
||||
...entry,
|
||||
percentageOfQuantity: entry.quantity / totalQuantity,
|
||||
}));
|
||||
|
||||
return _entries.map((entry) => ({
|
||||
...entry,
|
||||
cost: round(entry.percentageOfQuantity * total, 2),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/AccountDrawer.scss';
|
||||
|
||||
import { AccountDrawerProvider } from './AccountDrawerProvider';
|
||||
import AccountDrawerDetails from './AccountDrawerDetails';
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import AccountDrawerHeader from './AccountDrawerHeader';
|
||||
import AccountDrawerTable from './AccountDrawerTable';
|
||||
import { useAccountDrawerContext } from './AccountDrawerProvider';
|
||||
|
||||
import 'style/components/Drawer/AccountDrawer.scss';
|
||||
|
||||
/**
|
||||
* Account view details.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import BillTransactionDeleteAlert from 'containers/Alerts/Bills/BillTransactionDeleteAlert';
|
||||
import BillLocatedLandedCostDeleteAlert from 'containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert';
|
||||
|
||||
/**
|
||||
* Bill drawer alert.
|
||||
@@ -7,7 +7,7 @@ import BillTransactionDeleteAlert from 'containers/Alerts/Bills/BillTransactionD
|
||||
export default function BillDrawerAlerts() {
|
||||
return (
|
||||
<div class="bills-alerts">
|
||||
<BillTransactionDeleteAlert name="transaction-delete" />
|
||||
<BillLocatedLandedCostDeleteAlert name="bill-located-cost-delete" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/BillDrawer.scss';
|
||||
|
||||
import { BillDrawerProvider } from './BillDrawerProvider';
|
||||
import BillDrawerDetails from './BillDrawerDetails';
|
||||
import BillDrawerAlerts from './BillDrawerAlerts';
|
||||
|
||||
@@ -4,8 +4,6 @@ import intl from 'react-intl-universal';
|
||||
|
||||
import LocatedLandedCostTable from './LocatedLandedCostTable';
|
||||
|
||||
import 'style/components/Drawer/BillDrawer.scss';
|
||||
|
||||
/**
|
||||
* Bill view details.
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,9 @@ function BillDrawerProvider({ billId, ...props }) {
|
||||
//provider.
|
||||
const provider = {
|
||||
transactions,
|
||||
billId,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider loading={isLandedCostLoading}>
|
||||
<DrawerHeaderContent
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import React from 'react';
|
||||
import { DataTable } from 'components';
|
||||
import { DataTable, Card } from 'components';
|
||||
import { Button, Classes, NavbarGroup } from '@blueprintjs/core';
|
||||
|
||||
import { useLocatedLandedCostColumns, ActionsMenu } from './components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import Icon from 'components/Icon';
|
||||
|
||||
/**
|
||||
* Located landed cost table.
|
||||
@@ -13,25 +19,73 @@ import { compose } from 'utils';
|
||||
function LocatedLandedCostTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
const columns = useLocatedLandedCostColumns();
|
||||
const { transactions } = useBillDrawerContext();
|
||||
const { transactions, billId } = useBillDrawerContext();
|
||||
|
||||
// Handle the transaction delete action.
|
||||
const handleDeleteTransaction = ({ id }) => {
|
||||
openAlert('transaction-delete', { BillId: id });
|
||||
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;
|
||||
|
||||
switch (from_transaction_type) {
|
||||
case 'Expense':
|
||||
openDrawer('expense-drawer', { expenseId: from_transaction_id });
|
||||
break;
|
||||
|
||||
case 'Bill':
|
||||
default:
|
||||
openDrawer('bill-drawer', { billId: from_transaction_id });
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={transactions}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteTransaction,
|
||||
}}
|
||||
/>
|
||||
<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}
|
||||
payload={{
|
||||
onDelete: handleDeleteTransaction,
|
||||
onFromTranscationClick: handleFromTransactionClick,
|
||||
}}
|
||||
className={'datatable--landed-cost-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(LocatedLandedCostTable);
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
withDrawerActions,
|
||||
)(LocatedLandedCostTable);
|
||||
|
||||
@@ -3,7 +3,6 @@ import intl from 'react-intl-universal';
|
||||
import { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||
import { safeCallback } from 'utils';
|
||||
import { Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
@@ -20,22 +19,57 @@ export function ActionsMenu({ row: { original }, payload: { onDelete } }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function useLocatedLandedCostColumns() {
|
||||
return React.useMemo(() => [
|
||||
{
|
||||
Header: intl.get('name'),
|
||||
accessor: 'description',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
Header: intl.get('allocation_method'),
|
||||
accessor: 'allocation_method',
|
||||
width: 100,
|
||||
},
|
||||
]);
|
||||
/**
|
||||
* From transaction table cell.
|
||||
*/
|
||||
export function FromTransactionCell({
|
||||
row: { original },
|
||||
payload: { onFromTranscationClick }
|
||||
}) {
|
||||
// Handle the link click
|
||||
const handleAnchorClick = () => {
|
||||
onFromTranscationClick && onFromTranscationClick(original);
|
||||
};
|
||||
|
||||
return (
|
||||
<a href="#" onClick={handleAnchorClick}>
|
||||
{original.from_transaction_type} → {original.from_transaction_id}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bill located landed cost table columns.
|
||||
*/
|
||||
export function useLocatedLandedCostColumns() {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('name'),
|
||||
accessor: 'description',
|
||||
width: 150,
|
||||
className: 'name',
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_amount',
|
||||
width: 100,
|
||||
className: 'amount',
|
||||
},
|
||||
{
|
||||
id: 'from_transaction',
|
||||
Header: intl.get('From transaction'),
|
||||
Cell: FromTransactionCell,
|
||||
width: 100,
|
||||
className: 'from-transaction',
|
||||
},
|
||||
{
|
||||
Header: intl.get('allocation_method'),
|
||||
accessor: 'allocation_method_formatted',
|
||||
width: 100,
|
||||
className: 'allocation-method',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/ViewDetails.scss';
|
||||
|
||||
import { ExpenseDrawerProvider } from './ExpenseDrawerProvider';
|
||||
import ExpenseDrawerDetails from './ExpenseDrawerDetails';
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import ExpenseDrawerHeader from './ExpenseDrawerHeader';
|
||||
import ExpenseDrawerTable from './ExpenseDrawerTable';
|
||||
import ExpenseDrawerFooter from './ExpenseDrawerFooter';
|
||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||
import 'style/components/Drawer/ViewDetails.scss';
|
||||
|
||||
/**
|
||||
* Expense view details.
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/ViewDetails.scss';
|
||||
|
||||
import { ManualJournalDrawerProvider } from './ManualJournalDrawerProvider';
|
||||
import ManualJournalDrawerDetails from './ManualJournalDrawerDetails';
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import ManualJournalDrawerFooter from './ManualJournalDrawerFooter';
|
||||
|
||||
import { useManualJournalDrawerContext } from 'containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider';
|
||||
|
||||
import 'style/components/Drawer/ViewDetails.scss';
|
||||
|
||||
/**
|
||||
* Manual journal view details.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useJournal } from 'hooks/query';
|
||||
import { DashboardInsider, DrawerHeaderContent } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
const ManualJournalDrawerContext = React.createContext();
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ export default function ManualJournalDrawerTable({
|
||||
return (
|
||||
<div className="journal-drawer__content--table">
|
||||
<DataTable columns={columns} data={entries} />
|
||||
|
||||
<If condition={description}>
|
||||
<p className={'desc'}>
|
||||
<b>Description</b>: {description}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/DrawerTemplate.scss';
|
||||
|
||||
import PaperTemplateHeader from './PaperTemplateHeader';
|
||||
import PaperTemplateTable from './PaperTemplateTable';
|
||||
import PaperTemplateFooter from './PaperTemplateFooter';
|
||||
import { updateItemsEntriesTotal } from 'containers/Entries/utils';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import 'style/components/Drawer/DrawerTemplate.scss';
|
||||
|
||||
function PaperTemplate({ labels: propLabels, paperData, entries }) {
|
||||
const labels = {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/components/Drawers/DrawerTemplate.scss';
|
||||
|
||||
import PaymentPaperTemplateHeader from './PaymentPaperTemplateHeader';
|
||||
import PaymentPaperTemplateTable from './PaymentPaperTemplateTable';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import 'style/components/Drawer/DrawerTemplate.scss';
|
||||
|
||||
export default function PaymentPaperTemplate({
|
||||
labels: propLabels,
|
||||
|
||||
@@ -30,6 +30,7 @@ function ItemsEntriesTable({
|
||||
linesNumber,
|
||||
currencyCode,
|
||||
itemType, // sellable or purchasable
|
||||
landedCost = false
|
||||
}) {
|
||||
const [rows, setRows] = React.useState(initialEntries);
|
||||
const [rowItem, setRowItem] = React.useState(null);
|
||||
@@ -94,7 +95,7 @@ function ItemsEntriesTable({
|
||||
}, [entries, rows]);
|
||||
|
||||
// Editiable items entries columns.
|
||||
const columns = useEditableItemsEntriesColumns();
|
||||
const columns = useEditableItemsEntriesColumns({ landedCost });
|
||||
|
||||
// Handles the editor data update.
|
||||
const handleUpdateData = useCallback(
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ItemsListCell,
|
||||
PercentFieldCell,
|
||||
NumericInputCell,
|
||||
CheckBoxFieldCell,
|
||||
} from 'components/DataTableCells';
|
||||
|
||||
/**
|
||||
@@ -90,27 +91,18 @@ export function IndexTableCell({ row: { index } }) {
|
||||
return <span>{index + 1}</span>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Landed cost cell.
|
||||
*/
|
||||
const LandedCostCell = ({
|
||||
row: { index },
|
||||
column: { id },
|
||||
cell: { value: initialValue },
|
||||
data,
|
||||
payload,
|
||||
}) => {
|
||||
return <Checkbox minimal={true} className="ml2" />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Landed cost header cell.
|
||||
*/
|
||||
const LandedCostHeaderCell = () => {
|
||||
return (
|
||||
<>
|
||||
<T id={'cost'} />
|
||||
<Hint content={''} />
|
||||
<T id={'Landed'} />
|
||||
<Hint
|
||||
content={
|
||||
'This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.'
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -118,7 +110,7 @@ const LandedCostHeaderCell = () => {
|
||||
/**
|
||||
* Retrieve editable items entries columns.
|
||||
*/
|
||||
export function useEditableItemsEntriesColumns() {
|
||||
export function useEditableItemsEntriesColumns({ landedCost }) {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -182,14 +174,19 @@ export function useEditableItemsEntriesColumns() {
|
||||
width: 100,
|
||||
className: 'total',
|
||||
},
|
||||
{
|
||||
Header: '',
|
||||
accessor: 'landed_cost',
|
||||
Cell: LandedCostCell,
|
||||
width: 70,
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
},
|
||||
...(landedCost
|
||||
? [
|
||||
{
|
||||
Header: LandedCostHeaderCell,
|
||||
accessor: 'landed_cost',
|
||||
Cell: CheckBoxFieldCell,
|
||||
width: 100,
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
className: 'landed-cost',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
Header: '',
|
||||
accessor: 'action',
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { FastField } from 'formik';
|
||||
import React from 'react';
|
||||
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
|
||||
import { defaultExpenseEntry } from './utils';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
import { defaultExpenseEntry, accountsFieldShouldUpdate } from './utils';
|
||||
|
||||
/**
|
||||
* Expense form entries field.
|
||||
*/
|
||||
export default function ExpenseFormEntriesField({ linesNumber = 4 }) {
|
||||
// Expense form context.
|
||||
const { accounts } = useExpenseFormContext();
|
||||
|
||||
return (
|
||||
<FastField name={'categories'}>
|
||||
<FastField
|
||||
name={'categories'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
|
||||
@@ -22,12 +22,13 @@ export default function ExpenseFormEntriesTable({
|
||||
error,
|
||||
onChange,
|
||||
currencyCode,
|
||||
landedCost = true,
|
||||
}) {
|
||||
// Expense form context.
|
||||
const { accounts } = useExpenseFormContext();
|
||||
|
||||
// Memorized data table columns.
|
||||
const columns = useExpenseFormTableColumns();
|
||||
const columns = useExpenseFormTableColumns({ landedCost });
|
||||
|
||||
// Handles update datatable data.
|
||||
const handleUpdateData = useCallback(
|
||||
@@ -61,6 +62,7 @@ export default function ExpenseFormEntriesTable({
|
||||
|
||||
return (
|
||||
<DataTableEditable
|
||||
name={'expense-form'}
|
||||
columns={columns}
|
||||
data={entries}
|
||||
sticky={true}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
inputIntent,
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { customersFieldShouldUpdate, accountsFieldShouldUpdate } from './utils';
|
||||
import {
|
||||
CurrencySelectList,
|
||||
ContactSelecetList,
|
||||
@@ -51,7 +52,11 @@ export default function ExpenseFormHeader() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'payment_account_id'}>
|
||||
<FastField
|
||||
name={'payment_account_id'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
@@ -118,7 +123,11 @@ export default function ExpenseFormHeader() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'customer_id'}>
|
||||
<FastField
|
||||
name={'customer_id'}
|
||||
customers={customers}
|
||||
shouldUpdate={customersFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer'} />}
|
||||
|
||||
@@ -56,8 +56,12 @@ const ActionsCellRenderer = ({
|
||||
const LandedCostHeaderCell = () => {
|
||||
return (
|
||||
<>
|
||||
<T id={'cost'} />
|
||||
<Hint content={''} />
|
||||
<T id={'Landed'} />
|
||||
<Hint
|
||||
content={
|
||||
'This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.'
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -87,7 +91,7 @@ function ExpenseAccountFooterCell() {
|
||||
/**
|
||||
* Retrieve expense form table entries columns.
|
||||
*/
|
||||
export function useExpenseFormTableColumns() {
|
||||
export function useExpenseFormTableColumns({ landedCost }) {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -127,15 +131,19 @@ export function useExpenseFormTableColumns() {
|
||||
className: 'description',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
Header: LandedCostHeaderCell,
|
||||
accessor: 'landed_cost',
|
||||
Cell: CheckBoxFieldCell,
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 70,
|
||||
className: 'landed_cost',
|
||||
},
|
||||
...(landedCost
|
||||
? [
|
||||
{
|
||||
Header: LandedCostHeaderCell,
|
||||
accessor: 'landed_cost',
|
||||
Cell: CheckBoxFieldCell,
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 100,
|
||||
className: 'landed-cost',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
Header: '',
|
||||
accessor: 'action',
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { AppToaster } from 'components';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { transformToForm, repeatValue } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transformToForm,
|
||||
repeatValue,
|
||||
} from 'utils';
|
||||
|
||||
const ERROR = {
|
||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||
@@ -27,7 +31,7 @@ export const defaultExpenseEntry = {
|
||||
amount: '',
|
||||
expense_account_id: '',
|
||||
description: '',
|
||||
landed_cost: 0,
|
||||
landed_cost: false,
|
||||
};
|
||||
|
||||
export const defaultExpense = {
|
||||
@@ -62,3 +66,23 @@ export const transformToEditForm = (
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmine cusotmers fast-field should update.
|
||||
*/
|
||||
export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.customers !== oldProps.customers ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmine accounts fast-field should update.
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { useFormikContext, FastField, Field, ErrorMessage } from 'formik';
|
||||
import {
|
||||
FormGroup,
|
||||
Classes,
|
||||
@@ -23,12 +23,21 @@ import withSettings from 'containers/Settings/withSettings';
|
||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
||||
|
||||
import { compose, inputIntent } from 'utils';
|
||||
import {
|
||||
sellDescriptionFieldShouldUpdate,
|
||||
sellAccountFieldShouldUpdate,
|
||||
sellPriceFieldShouldUpdate,
|
||||
costPriceFieldShouldUpdate,
|
||||
costAccountFieldShouldUpdate,
|
||||
purchaseDescFieldShouldUpdate,
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Item form body.
|
||||
*/
|
||||
function ItemFormBody({ baseCurrency }) {
|
||||
const { accounts } = useItemFormContext();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
return (
|
||||
<div class="page-form__section page-form__section--selling-cost">
|
||||
@@ -53,7 +62,11 @@ function ItemFormBody({ baseCurrency }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Selling price ------------- */}
|
||||
<FastField name={'sell_price'}>
|
||||
<FastField
|
||||
name={'sell_price'}
|
||||
sellable={values.sellable}
|
||||
shouldUpdate={sellPriceFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'selling_price'} />}
|
||||
@@ -78,7 +91,12 @@ function ItemFormBody({ baseCurrency }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Selling account ------------- */}
|
||||
<FastField name={'sell_account_id'}>
|
||||
<FastField
|
||||
name={'sell_account_id'}
|
||||
sellable={values.sellable}
|
||||
accounts={accounts}
|
||||
shouldUpdate={sellAccountFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account'} />}
|
||||
@@ -107,7 +125,11 @@ function ItemFormBody({ baseCurrency }) {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'sell_description'}>
|
||||
<FastField
|
||||
name={'sell_description'}
|
||||
sellable={values.sellable}
|
||||
shouldUpdate={sellDescriptionFieldShouldUpdate}
|
||||
>
|
||||
{({ form: { values }, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
@@ -146,7 +168,11 @@ function ItemFormBody({ baseCurrency }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Cost price ------------- */}
|
||||
<FastField name={'cost_price'}>
|
||||
<FastField
|
||||
name={'cost_price'}
|
||||
purchasable={values.purchasable}
|
||||
shouldUpdate={costPriceFieldShouldUpdate}
|
||||
>
|
||||
{({ field, form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'cost_price'} />}
|
||||
@@ -171,7 +197,12 @@ function ItemFormBody({ baseCurrency }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Cost account ------------- */}
|
||||
<FastField name={'cost_account_id'}>
|
||||
<FastField
|
||||
name={'cost_account_id'}
|
||||
purchasable={values.purchasable}
|
||||
accounts={accounts}
|
||||
shouldUpdate={costAccountFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account'} />}
|
||||
@@ -200,7 +231,11 @@ function ItemFormBody({ baseCurrency }) {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'purchase_description'}>
|
||||
<FastField
|
||||
name={'purchase_description'}
|
||||
purchasable={values.purchasable}
|
||||
shouldUpdate={purchaseDescFieldShouldUpdate}
|
||||
>
|
||||
{({ form: { values }, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
|
||||
@@ -8,6 +8,7 @@ import classNames from 'classnames';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { accountsFieldShouldUpdate } from './utils';
|
||||
import { compose, inputIntent } from 'utils';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { useItemFormContext } from './ItemFormProvider';
|
||||
@@ -27,7 +28,11 @@ function ItemFormInventorySection({ baseCurrency }) {
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
{/*------------- Inventory account ------------- */}
|
||||
<FastField name={'inventory_account_id'}>
|
||||
<FastField
|
||||
name={'inventory_account_id'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'inventory_account'} />}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { CLASSES } from 'common/classes';
|
||||
|
||||
import { useItemFormContext } from './ItemFormProvider';
|
||||
import { handleStringChange, inputIntent } from 'utils';
|
||||
import { categoriesFieldShouldUpdate } from './utils';
|
||||
|
||||
/**
|
||||
* Item form primary section.
|
||||
@@ -130,7 +131,11 @@ export default function ItemFormPrimarySection() {
|
||||
</FastField>
|
||||
|
||||
{/*----------- Item category ----------*/}
|
||||
<FastField name={'category_id'}>
|
||||
<FastField
|
||||
name={'category_id'}
|
||||
categories={itemsCategories}
|
||||
shouldUpdate={categoriesFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'category'} />}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { defaultFastFieldShouldUpdate } from 'utils';
|
||||
|
||||
export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
|
||||
const table = {
|
||||
@@ -28,7 +29,9 @@ export const handleDeleteErrors = (errors) => {
|
||||
)
|
||||
) {
|
||||
AppToaster.show({
|
||||
message: intl.get('you_could_not_delete_item_that_has_associated_inventory_adjustments_transacions'),
|
||||
message: intl.get(
|
||||
'you_could_not_delete_item_that_has_associated_inventory_adjustments_transacions',
|
||||
),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
@@ -38,8 +41,83 @@ export const handleDeleteErrors = (errors) => {
|
||||
)
|
||||
) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_change_item_type_to_inventory_with_item_has_associated_transactions'),
|
||||
message: intl.get(
|
||||
'cannot_change_item_type_to_inventory_with_item_has_associated_transactions',
|
||||
),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines accounts fast field should update.
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines categories fast field should update.
|
||||
*/
|
||||
export const categoriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.categories !== oldProps.categories ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sell price fast field should update.
|
||||
*/
|
||||
export const sellPriceFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.sellable !== oldProps.sellable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sell account fast field should update.
|
||||
*/
|
||||
export const sellAccountFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.sellable !== oldProps.sellable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sell description fast field should update.
|
||||
*/
|
||||
export const sellDescriptionFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.sellable !== oldProps.sellable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
export const costAccountFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.purchasable !== oldProps.purchasable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
export const costPriceFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.purchasable !== oldProps.purchasable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
export const purchaseDescFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.purchasable !== oldProps.purchasable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ function BillForm({
|
||||
currency_code: baseCurrency,
|
||||
}),
|
||||
}),
|
||||
[bill],
|
||||
[bill, baseCurrency],
|
||||
);
|
||||
|
||||
// Transform response error to fields.
|
||||
|
||||
@@ -7,6 +7,7 @@ import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { ContactSelecetList, FieldRequiredHint, Icon } from 'components';
|
||||
import { vendorsFieldShouldUpdate } from './utils';
|
||||
|
||||
import { useBillFormContext } from './BillFormProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
@@ -28,7 +29,11 @@ function BillFormHeader() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------- Vendor name ------ */}
|
||||
<FastField name={'vendor_id'}>
|
||||
<FastField
|
||||
name={'vendor_id'}
|
||||
vendors={vendors}
|
||||
shouldUpdate={vendorsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'vendor_name'} />}
|
||||
|
||||
@@ -4,13 +4,23 @@ import { FastField } from 'formik';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useBillFormContext } from './BillFormProvider';
|
||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||
import {
|
||||
entriesFieldShouldUpdate
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Bill form body.
|
||||
*/
|
||||
export default function BillFormBody({ defaultBill }) {
|
||||
const { items } = useBillFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
<FastField
|
||||
name={'entries'}
|
||||
items={items}
|
||||
shouldUpdate={entriesFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
@@ -25,6 +35,7 @@ export default function BillFormBody({ defaultBill }) {
|
||||
errors={error}
|
||||
linesNumber={4}
|
||||
currencyCode={values.currency_code}
|
||||
landedCost={true}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
@@ -2,7 +2,11 @@ import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { transformToForm, repeatValue } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transformToForm,
|
||||
repeatValue,
|
||||
} from 'utils';
|
||||
|
||||
export const MIN_LINES_NUMBER = 4;
|
||||
|
||||
@@ -13,6 +17,7 @@ export const defaultBillEntry = {
|
||||
discount: '',
|
||||
quantity: '',
|
||||
description: '',
|
||||
landed_cost: false,
|
||||
};
|
||||
|
||||
export const defaultBill = {
|
||||
@@ -62,3 +67,23 @@ export const handleDeleteErrors = (errors) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines vendors fast field should update
|
||||
*/
|
||||
export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.vendors !== oldProps.vendors ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines entries fast field should update.
|
||||
*/
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ export function ActionsMenu({
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
// icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
||||
icon={<Icon icon="receipt-24" iconSize={16} />}
|
||||
text={intl.get('allocate_landed_coast')}
|
||||
onClick={safeCallback(onAllocateLandedCost, original)}
|
||||
/>
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
fullAmountPaymentEntries,
|
||||
amountPaymentEntries,
|
||||
} from 'utils';
|
||||
import { accountsFieldShouldUpdate, vendorsFieldShouldUpdate } from './utils';
|
||||
|
||||
/**
|
||||
* Payment made form header fields.
|
||||
@@ -48,17 +49,14 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||
} = useFormikContext();
|
||||
|
||||
// Payment made form context.
|
||||
const {
|
||||
vendors,
|
||||
accounts,
|
||||
isNewMode,
|
||||
setPaymentVendorId,
|
||||
} = usePaymentMadeFormContext();
|
||||
const { vendors, accounts, isNewMode, setPaymentVendorId } =
|
||||
usePaymentMadeFormContext();
|
||||
|
||||
// Sumation of payable full-amount.
|
||||
const payableFullAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [
|
||||
entries,
|
||||
]);
|
||||
const payableFullAmount = useMemo(
|
||||
() => safeSumBy(entries, 'due_amount'),
|
||||
[entries],
|
||||
);
|
||||
|
||||
// Handle receive full-amount click.
|
||||
const handleReceiveFullAmountClick = () => {
|
||||
@@ -78,7 +76,11 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------ Vendor name ------------ */}
|
||||
<FastField name={'vendor_id'}>
|
||||
<FastField
|
||||
name={'vendor_id'}
|
||||
vendors={vendors}
|
||||
shouldUpdate={vendorsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'vendor_name'} />}
|
||||
@@ -157,7 +159,7 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||
small={true}
|
||||
minimal={true}
|
||||
>
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<Money amount={payableFullAmount} currency={baseCurrency} />)
|
||||
</Button>
|
||||
</FormGroup>
|
||||
@@ -184,7 +186,11 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment account ------------ */}
|
||||
<FastField name={'payment_account_id'}>
|
||||
<FastField
|
||||
name={'payment_account_id'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import moment from 'moment';
|
||||
import { safeSumBy, transformToForm } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
safeSumBy,
|
||||
transformToForm,
|
||||
} from 'utils';
|
||||
|
||||
export const ERRORS = {
|
||||
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
||||
@@ -9,10 +13,10 @@ export const ERRORS = {
|
||||
export const defaultPaymentMadeEntry = {
|
||||
bill_id: '',
|
||||
payment_amount: '',
|
||||
currency_code:'',
|
||||
currency_code: '',
|
||||
id: null,
|
||||
due_amount: null,
|
||||
amount:''
|
||||
amount: '',
|
||||
};
|
||||
|
||||
// Default initial values of payment made.
|
||||
@@ -48,7 +52,26 @@ export const transformToNewPageEntries = (entries) => {
|
||||
return entries.map((entry) => ({
|
||||
...transformToForm(entry, defaultPaymentMadeEntry),
|
||||
payment_amount: '',
|
||||
currency_code:entry.currency_code,
|
||||
|
||||
currency_code: entry.currency_code,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines vendors fast field when update.
|
||||
*/
|
||||
export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.vendors !== oldProps.vendors ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines accounts fast field when update.
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ function EstimateFormHeader({
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<EstimateFormHeaderFields />
|
||||
|
||||
<PageFormBigNumber
|
||||
label={intl.get('amount')}
|
||||
amount={totalDueAmount}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
inputIntent,
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { customersFieldShouldUpdate } from './utils';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
@@ -67,7 +68,11 @@ function EstimateFormHeader({
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ----------- Customer name ----------- */}
|
||||
<FastField name={'customer_id'}>
|
||||
<FastField
|
||||
name={'customer_id'}
|
||||
customers={customers}
|
||||
shouldUpdate={customersFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
@@ -170,7 +175,9 @@ function EstimateFormHeader({
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: <T id={'setting_your_auto_generated_estimate_number'}/>,
|
||||
content: (
|
||||
<T id={'setting_your_auto_generated_estimate_number'} />
|
||||
),
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,6 +4,7 @@ import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||
import { entriesFieldShouldUpdate } from './utils';
|
||||
|
||||
/**
|
||||
* Estimate form items entries editor.
|
||||
@@ -13,7 +14,11 @@ export default function EstimateFormItemsEntriesField() {
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
<FastField
|
||||
name={'entries'}
|
||||
items={items}
|
||||
shouldUpdate={entriesFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { transactionNumber, repeatValue, transformToForm } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transactionNumber,
|
||||
repeatValue,
|
||||
transformToForm,
|
||||
} from 'utils';
|
||||
|
||||
export const MIN_LINES_NUMBER = 4;
|
||||
|
||||
@@ -49,4 +54,24 @@ export const useObserveEstimateNoSettings = (prefix, nextNumber) => {
|
||||
const estimateNo = transactionNumber(prefix, nextNumber);
|
||||
setFieldValue('estimate_number', estimateNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines customers fast field when update.
|
||||
*/
|
||||
export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.customers !== oldProps.customers ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines entries fast field should update.
|
||||
*/
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,10 @@ import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||
import classNames from 'classnames';
|
||||
import { useObserveInvoiceNoSettings } from './utils';
|
||||
import {
|
||||
useObserveInvoiceNoSettings,
|
||||
customerNameFieldShouldUpdate,
|
||||
} from './utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
ContactSelecetList,
|
||||
@@ -58,15 +61,16 @@ function InvoiceFormHeaderFields({
|
||||
};
|
||||
|
||||
// Syncs invoice number settings with form.
|
||||
useObserveInvoiceNoSettings(
|
||||
invoiceNumberPrefix,
|
||||
invoiceNextNumber,
|
||||
);
|
||||
useObserveInvoiceNoSettings(invoiceNumberPrefix, invoiceNextNumber);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ----------- Customer name ----------- */}
|
||||
<FastField name={'customer_id'}>
|
||||
<FastField
|
||||
name={'customer_id'}
|
||||
customers={customers}
|
||||
shouldUpdate={customerNameFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
@@ -168,7 +172,9 @@ function InvoiceFormHeaderFields({
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: <T id={'setting_your_auto_generated_invoice_number'}/>,
|
||||
content: (
|
||||
<T id={'setting_your_auto_generated_invoice_number'} />
|
||||
),
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,6 +4,7 @@ import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
import { entriesFieldShouldUpdate } from './utils';
|
||||
|
||||
/**
|
||||
* Invoice items entries editor field.
|
||||
@@ -13,7 +14,11 @@ export default function InvoiceItemsEntriesEditorField() {
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
<FastField
|
||||
name={'entries'}
|
||||
items={items}
|
||||
shouldUpdate={entriesFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
|
||||
@@ -11,7 +11,7 @@ import { updateItemsEntriesTotal } from 'containers/Entries/utils';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
import { orderingLinesIndexes } from 'utils';
|
||||
import { defaultFastFieldShouldUpdate } from 'utils';
|
||||
import intl from 'react-intl-universal';
|
||||
import { ERROR } from 'common/errors';
|
||||
import { AppToaster } from 'components';
|
||||
@@ -100,4 +100,18 @@ export const useObserveInvoiceNoSettings = (prefix, nextNumber) => {
|
||||
const invoiceNo = transactionNumber(prefix, nextNumber);
|
||||
setFieldValue('invoice_no', invoiceNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
};
|
||||
|
||||
export const customerNameFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.customers !== oldProps.customers ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from 'components';
|
||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
@@ -41,6 +42,8 @@ import {
|
||||
useObservePaymentNoSettings,
|
||||
amountPaymentEntries,
|
||||
fullAmountPaymentEntries,
|
||||
customersFieldShouldUpdate,
|
||||
accountsFieldShouldUpdate,
|
||||
} from './utils';
|
||||
import { toSafeInteger } from 'lodash';
|
||||
|
||||
@@ -115,7 +118,11 @@ function PaymentReceiveHeaderFields({
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------- Customer name ------------- */}
|
||||
<FastField name={'customer_id'}>
|
||||
<FastField
|
||||
name={'customer_id'}
|
||||
customers={customers}
|
||||
shouldUpdate={customersFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
@@ -247,7 +254,11 @@ function PaymentReceiveHeaderFields({
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Deposit account ------------ */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
<FastField
|
||||
name={'deposit_account_id'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'deposit_to'} />}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { transactionNumber, transformToForm, safeSumBy } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transactionNumber,
|
||||
transformToForm,
|
||||
safeSumBy,
|
||||
} from 'utils';
|
||||
|
||||
// Default payment receive entry.
|
||||
export const defaultPaymentReceiveEntry = {
|
||||
@@ -99,3 +104,23 @@ export const useObservePaymentNoSettings = (prefix, nextNumber) => {
|
||||
setFieldValue('payment_receive_no', invoiceNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the customers fast-field should update.
|
||||
*/
|
||||
export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.customers !== oldProps.customers ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the accounts fast-field should update.
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -28,7 +28,11 @@ import {
|
||||
inputIntent,
|
||||
} from 'utils';
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
import { useObserveReceiptNoSettings } from './utils';
|
||||
import {
|
||||
accountsFieldShouldUpdate,
|
||||
customersFieldShouldUpdate,
|
||||
useObserveReceiptNoSettings,
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Receipt form header fields.
|
||||
@@ -70,7 +74,11 @@ function ReceiptFormHeader({
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ----------- Customer name ----------- */}
|
||||
<FastField name={'customer_id'}>
|
||||
<FastField
|
||||
name={'customer_id'}
|
||||
customers={customers}
|
||||
shouldUpdate={customersFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
@@ -94,7 +102,11 @@ function ReceiptFormHeader({
|
||||
</FastField>
|
||||
|
||||
{/* ----------- Deposit account ----------- */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
<FastField
|
||||
name={'deposit_account_id'}
|
||||
accounts={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'deposit_account'} />}
|
||||
|
||||
@@ -4,13 +4,14 @@ import { FastField } from 'formik';
|
||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
import { entriesFieldShouldUpdate } from './utils';
|
||||
|
||||
export default function ReceiptItemsEntriesEditor({ defaultReceipt }) {
|
||||
const { items } = useReceiptFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
<FastField name={'entries'} items={items} shouldUpdate={entriesFieldShouldUpdate}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { transactionNumber, repeatValue, transformToForm } from 'utils';
|
||||
import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transactionNumber,
|
||||
repeatValue,
|
||||
transformToForm,
|
||||
} from 'utils';
|
||||
|
||||
export const MIN_LINES_NUMBER = 4;
|
||||
|
||||
@@ -42,7 +47,6 @@ export const transformToEditForm = (receipt) => ({
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
@@ -50,4 +54,34 @@ export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
|
||||
const receiptNo = transactionNumber(prefix, nextNumber);
|
||||
setFieldValue('receipt_number', receiptNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines entries fast field should update.
|
||||
*/
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines accounts fast field should update.
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines customers fast field should update.
|
||||
*/
|
||||
export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.customers !== oldProps.customers ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1146,5 +1146,11 @@
|
||||
"No items": "No items",
|
||||
"cannot_delete_bill_that_has_associated_landed_cost_transactions": "Cannot delete bill that has associated landed cost transactions.",
|
||||
"couldn_t_delete_expense_transaction_has_associated_located_landed_cost_transaction": "Couldn't delete expense transaction has associated located landed cost transaction",
|
||||
"the_landed_cost_has_been_created_successfully": "The landed cost has been created successfully"
|
||||
"the_landed_cost_has_been_created_successfully": "The landed cost has been created successfully",
|
||||
"Select transaction": "Select transaction",
|
||||
"Select transaction entry": "Select transaction entry",
|
||||
"From transaction": "From transaction",
|
||||
"Landed": "Landed",
|
||||
"This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.": "This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.",
|
||||
"Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?": "Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?"
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
.th,
|
||||
.td {
|
||||
border-left: 1px dashed #e2e2e2;
|
||||
border-left: 1px solid #e2e2e2;
|
||||
|
||||
&.index {
|
||||
text-align: center;
|
||||
@@ -55,6 +55,19 @@
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.landed-cost{
|
||||
|
||||
.bp3-control{
|
||||
margin-top: 0;
|
||||
margin-left: 34px;
|
||||
}
|
||||
.bp3-control-indicator{
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tr {
|
||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
||||
|
||||
21
client/src/style/components/Details.scss
Normal file
21
client/src/style/components/Details.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.details-menu {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
|
||||
&.is-vertical {}
|
||||
|
||||
.detail-item {
|
||||
|
||||
|
||||
&__label {
|
||||
color: #666666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__content {
|
||||
text-transform: capitalize;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
client/src/style/components/Drawer.scss
Normal file
17
client/src/style/components/Drawer.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
.bp3-drawer {
|
||||
|
||||
|
||||
.bp3-drawer-header {
|
||||
margin-bottom: 2px;
|
||||
background-color: #FFF;
|
||||
|
||||
.bp3-heading {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bp3-heading,
|
||||
.bp3-icon {
|
||||
color: #354152;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,4 @@
|
||||
.bp3-drawer-header {
|
||||
box-shadow: 0 0 0;
|
||||
|
||||
.bp3-heading{
|
||||
font-size: 16px;
|
||||
}
|
||||
.bp3-button{
|
||||
min-height: 28px;
|
||||
min-width: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-drawer {
|
||||
background-color: #fbfbfb;
|
||||
@@ -94,29 +84,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-drawer.bp3-position-right {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bp3-drawer-header {
|
||||
margin-bottom: 2px;
|
||||
box-shadow: (0, 0, 0);
|
||||
background-color: #6a7993;
|
||||
|
||||
.bp3-heading,
|
||||
.bp3-icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,15 @@
|
||||
.bp3-tabs {
|
||||
.bp3-tab-list {
|
||||
position: relative;
|
||||
|
||||
background-color: #FFF;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: #f0f0f0;
|
||||
background: #e1e2e8;
|
||||
}
|
||||
|
||||
> *:not(:last-child) {
|
||||
@@ -29,14 +30,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tab-panel{
|
||||
margin-top: 0;
|
||||
|
||||
.card{
|
||||
margin: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bigcapital-datatable {
|
||||
.datatable--landed-cost-transactions {
|
||||
.table {
|
||||
max-height: 500px;
|
||||
border: 1px solid #d1dee2;
|
||||
min-width: auto;
|
||||
margin: 12px;
|
||||
|
||||
.tbody,
|
||||
.tbody-inner {
|
||||
@@ -48,34 +53,13 @@
|
||||
}
|
||||
.tbody {
|
||||
.tr .td {
|
||||
padding: 0.8rem;
|
||||
padding: 0.6rem;
|
||||
|
||||
&.amount{
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-drawer.bp3-position-right {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bp3-drawer-header {
|
||||
margin-bottom: 2px;
|
||||
box-shadow: (0, 0, 0);
|
||||
background-color: #6a7993;
|
||||
|
||||
.bp3-heading,
|
||||
.bp3-icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,7 @@
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
.bp3-drawer-header .bp3-heading {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -1,6 +1,5 @@
|
||||
.journal-drawer,
|
||||
.expense-drawer {
|
||||
background: #f5f5f5;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
@@ -18,8 +17,8 @@
|
||||
justify-content: flex-start;
|
||||
margin: 15px 0 20px;
|
||||
font-size: 14px;
|
||||
// color: #333333;
|
||||
color: #666666;
|
||||
|
||||
> div {
|
||||
flex-grow: 1;
|
||||
span {
|
||||
@@ -44,17 +43,17 @@
|
||||
&--table {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
|
||||
.table {
|
||||
color: #666666;
|
||||
font-size: 14px;
|
||||
.thead .tr .th .resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.thead .th {
|
||||
background: transparent;
|
||||
color: #222222;
|
||||
border-bottom: 1px solid #000000;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.thead .th,
|
||||
.tbody .tr .td {
|
||||
background: transparent;
|
||||
padding: 0.8rem 0.5rem;
|
||||
@@ -63,7 +62,6 @@
|
||||
.desc {
|
||||
margin: 20px 0 60px;
|
||||
|
||||
// margin: 20px 0;
|
||||
> b {
|
||||
color: #2f2f2f;
|
||||
}
|
||||
@@ -93,25 +91,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-drawer.bp3-position-right {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bp3-drawer-header {
|
||||
margin-bottom: 2px;
|
||||
box-shadow: (0, 0, 0);
|
||||
background-color: #6a7993;
|
||||
.bp3-heading,
|
||||
.bp3-icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ body.page-bill-edit{
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
|
||||
|
||||
.page-form--bill{
|
||||
$self: '.page-form';
|
||||
|
||||
@@ -36,7 +35,7 @@ body.page-bill-edit{
|
||||
max-width: 440px;
|
||||
}
|
||||
|
||||
&.form-group{
|
||||
&.form-group{
|
||||
|
||||
&--expiration-date{
|
||||
max-width: 340px;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
.dashboard__insider--expenses {
|
||||
|
||||
.dashboard__insider--expenses{
|
||||
.bigcapital-datatable {
|
||||
|
||||
.bigcapital-datatable{
|
||||
|
||||
.tbody{
|
||||
.tr .td.total_amount{
|
||||
span{
|
||||
.tbody {
|
||||
.tr .td.total_amount {
|
||||
span {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
@@ -13,36 +12,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
.page-form--expense{
|
||||
.page-form--expense {
|
||||
$self: '.page-form';
|
||||
|
||||
#{$self}__header{
|
||||
#{$self}__header {
|
||||
display: flex;
|
||||
|
||||
&-fields{
|
||||
&-fields {
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
.bp3-label{
|
||||
.bp3-label {
|
||||
min-width: 140px;
|
||||
}
|
||||
.bp3-form-content{
|
||||
|
||||
.bp3-form-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bp3-form-group{
|
||||
.bp3-form-group {
|
||||
margin-bottom: 18px;
|
||||
|
||||
&.bp3-inline{
|
||||
max-width: 440px;
|
||||
&.bp3-inline {
|
||||
max-width: 440px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group--description{
|
||||
.datatable-editor--expense-form {
|
||||
|
||||
|
||||
.table {
|
||||
|
||||
.tbody {
|
||||
.tr .td {
|
||||
|
||||
|
||||
&.landed-cost {
|
||||
|
||||
.bp3-control {
|
||||
margin-top: 0;
|
||||
margin-left: 34px;
|
||||
}
|
||||
|
||||
.bp3-control-indicator {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group--description {
|
||||
max-width: 500px;
|
||||
|
||||
textarea{
|
||||
textarea {
|
||||
min-height: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
|
||||
|
||||
// Noto Sans
|
||||
// -------------------------------------
|
||||
@font-face {
|
||||
font-family: Noto Sans;
|
||||
src: local('Noto Sans'), url('../fonts/NotoSans-SemiBold.woff') format('woff');
|
||||
@@ -30,46 +34,8 @@
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
// arabic regular
|
||||
@font-face {
|
||||
font-family: Noto Sans Arabic;
|
||||
src: local('Noto Sans'),
|
||||
url('../fonts/NotoSansArabicUI-SemiCondensed.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
// arabic black
|
||||
@font-face {
|
||||
font-family: Noto Sans Arabic;
|
||||
src: local('Noto Sans'),
|
||||
url('../fonts/NotoSansArabicUI-SemiCondensedBlack.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
//arabic Medium
|
||||
@font-face {
|
||||
font-family: Noto Sans Arabic;
|
||||
src: local('Noto Sans'),
|
||||
url('../fonts/NotoSansArabicUI-SemiCondensedMedium.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
//arabic SemiBold
|
||||
@font-face {
|
||||
font-family: Noto Sans Arabic;
|
||||
src: local('Noto Sans'),
|
||||
url('../fonts/NotoSansArabicUI-SemiCondensedSemiBold.woff') format('woff');
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
// Segoe UI Arabic
|
||||
// -------------------------------------
|
||||
// Segoe UI Arabic - Regular
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
|
||||
@@ -16,7 +16,7 @@ $menu-item-color-active: $light-gray3;
|
||||
$breadcrumbs-collapsed-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#6B8193' enable-background='new 0 0 16 16' xml:space='preserve'><g><circle cx='2' cy='8.03' r='2'/><circle cx='14' cy='8.03' r='2'/><circle cx='8' cy='8.03' r='2'/></g></svg>");
|
||||
|
||||
$sidebar-zindex: 15;
|
||||
$pt-font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont,
|
||||
$pt-font-family: 'Noto Sans', -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue,
|
||||
Icons16, sans-serif;
|
||||
|
||||
|
||||
@@ -90,7 +90,9 @@ export const objectKeysTransform = (obj, transform) => {
|
||||
|
||||
export const compose = (...funcs) =>
|
||||
funcs.reduce(
|
||||
(a, b) => (...args) => a(b(...args)),
|
||||
(a, b) =>
|
||||
(...args) =>
|
||||
a(b(...args)),
|
||||
(arg) => arg,
|
||||
);
|
||||
|
||||
@@ -639,7 +641,32 @@ const getCurrenciesOptions = () => {
|
||||
currency_code: currencyCode,
|
||||
formatted_name: `${currencyCode} - ${currency.name}`,
|
||||
};
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export const currenciesOptions = getCurrenciesOptions();
|
||||
|
||||
/**
|
||||
* Deeply get a value from an object via its path.
|
||||
*/
|
||||
function getIn(obj, key, def, p = 0) {
|
||||
const path = _.toPath(key);
|
||||
while (obj && p < path.length) {
|
||||
obj = obj[path[p++]];
|
||||
}
|
||||
return obj === undefined ? def : obj;
|
||||
}
|
||||
|
||||
export const currenciesOptions = getCurrenciesOptions();
|
||||
export const defaultFastFieldShouldUpdate = (props, prevProps) => {
|
||||
return (
|
||||
props.name !== prevProps.name ||
|
||||
getIn(props.formik.values, prevProps.name) !==
|
||||
getIn(prevProps.formik.values, prevProps.name) ||
|
||||
getIn(props.formik.errors, prevProps.name) !==
|
||||
getIn(prevProps.formik.errors, prevProps.name) ||
|
||||
getIn(props.formik.touched, prevProps.name) !==
|
||||
getIn(prevProps.formik.touched, prevProps.name) ||
|
||||
Object.keys(prevProps).length !== Object.keys(props).length ||
|
||||
props.formik.isSubmitting !== prevProps.formik.isSubmitting
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user