From 9baf81f80385d11a2d1c012e0fddeb762be3e31b Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Mon, 26 Jul 2021 19:45:16 +0200 Subject: [PATCH] fix: FastField re-rendering. fix: Allocate landed cost dialog. --- client/src/components/Card.js | 5 ++ .../components/Datatable/DatatableEditable.js | 14 +++- client/src/components/Details/index.js | 29 +++++++ client/src/components/Drawer/Drawer.js | 7 +- client/src/components/index.js | 4 +- .../Accounting/JournalsLanding/utils.js | 2 +- .../MakeJournal/MakeJournalEntriesField.js | 18 +++- .../MakeJournalEntriesHeaderFields.js | 11 ++- .../Accounting/MakeJournal/utils.js | 33 ++++++-- ...js => BillLocatedLandedCostDeleteAlert.js} | 2 +- .../AllocateLandedCostForm.js | 7 +- .../AllocateLandedCostForm.schema.js | 31 ++++--- .../AllocateLandedCostFormFields.js | 50 ++++++++--- .../Dialogs/AllocateLandedCostDialog/utils.js | 55 +++++++++++++ .../AccountDrawer/AccountDrawerContent.js | 3 + .../AccountDrawer/AccountDrawerDetails.js | 2 - .../Drawers/BillDrawer/BillDrawerAlerts.js | 4 +- .../Drawers/BillDrawer/BillDrawerContent.js | 3 + .../Drawers/BillDrawer/BillDrawerDetails.js | 2 - .../Drawers/BillDrawer/BillDrawerProvider.js | 2 + .../BillDrawer/LocatedLandedCostTable.js | 78 +++++++++++++++--- .../Drawers/BillDrawer/components.js | 72 +++++++++++----- .../ExpenseDrawer/ExpenseDrawerContent.js | 3 + .../ExpenseDrawer/ExpenseDrawerDetails.js | 1 - .../ManualJournalDrawerContent.js | 3 + .../ManualJournalDrawerDetails.js | 2 - .../ManualJournalDrawerProvider.js | 2 +- .../ManualJournalDrawerTable.js | 1 + .../Drawers/PaperTemplate/PaperTemplate.js | 4 +- .../PaymentPaperTemplate.js | 4 +- .../containers/Entries/ItemsEntriesTable.js | 3 +- client/src/containers/Entries/components.js | 45 +++++----- .../ExpenseForm/ExpenseFormEntriesField.js | 12 ++- .../ExpenseForm/ExpenseFormEntriesTable.js | 4 +- .../ExpenseForm/ExpenseFormHeaderFields.js | 13 ++- .../Expenses/ExpenseForm/components.js | 32 +++++--- .../containers/Expenses/ExpenseForm/utils.js | 28 ++++++- client/src/containers/Items/ItemFormBody.js | 49 +++++++++-- .../Items/ItemFormInventorySection.js | 7 +- .../Items/ItemFormPrimarySection.js | 7 +- client/src/containers/Items/utils.js | 82 ++++++++++++++++++- .../Purchases/Bills/BillForm/BillForm.js | 2 +- .../Bills/BillForm/BillFormHeaderFields.js | 7 +- .../Bills/BillForm/BillItemsEntriesEditor.js | 13 ++- .../Purchases/Bills/BillForm/utils.js | 27 +++++- .../Bills/BillsLanding/components.js | 2 +- .../PaymentMadeFormHeaderFields.js | 30 ++++--- .../PaymentMades/PaymentForm/utils.js | 35 ++++++-- .../EstimateForm/EstimateFormHeader.js | 1 + .../EstimateForm/EstimateFormHeaderFields.js | 11 ++- .../EstimateForm/EstimateItemsEntriesField.js | 7 +- .../Sales/Estimates/EstimateForm/utils.js | 29 ++++++- .../InvoiceForm/InvoiceFormHeaderFields.js | 20 +++-- .../InvoiceItemsEntriesEditorField.js | 7 +- .../Sales/Invoices/InvoiceForm/utils.js | 18 +++- .../PaymentReceiveHeaderFields.js | 15 +++- .../PaymentReceiveForm/utils.js | 27 +++++- .../ReceiptForm/ReceiptFormHeaderFields.js | 18 +++- .../ReceiptForm/ReceiptItemsEntriesEditor.js | 3 +- .../Sales/Receipts/ReceiptForm/utils.js | 40 ++++++++- client/src/lang/en/index.json | 8 +- .../DataTable/DataTableEditable.scss | 15 +++- client/src/style/components/Details.scss | 21 +++++ client/src/style/components/Drawer.scss | 17 ++++ .../{Drawer => Drawers}/AccountDrawer.scss | 35 -------- .../{Drawer => Drawers}/BillDrawer.scss | 52 ++++-------- .../{Drawer => Drawers}/DrawerTemplate.scss | 1 + .../{Drawer => Drawers}/ViewDetails.scss | 34 ++------ client/src/style/pages/Bills/PageForm.scss | 3 +- client/src/style/pages/Expense/PageForm.scss | 59 +++++++++---- client/src/style/pages/fonts.scss | 46 ++--------- client/src/style/variables.scss | 2 +- client/src/utils.js | 33 +++++++- .../FinancialStatements/CashFlow/CashFlow.ts | 1 - .../VendorBalanceSummary/index.ts | 16 ++-- .../api/controllers/Purchases/LandedCost.ts | 5 +- .../Purchases/LandedCost/LandedCostListing.ts | 14 +++- 77 files changed, 1046 insertions(+), 364 deletions(-) create mode 100644 client/src/components/Card.js create mode 100644 client/src/components/Details/index.js rename client/src/containers/Alerts/Bills/{BillTransactionDeleteAlert.js => BillLocatedLandedCostDeleteAlert.js} (91%) create mode 100644 client/src/style/components/Details.scss create mode 100644 client/src/style/components/Drawer.scss rename client/src/style/components/{Drawer => Drawers}/AccountDrawer.scss (72%) rename client/src/style/components/{Drawer => Drawers}/BillDrawer.scss (60%) rename client/src/style/components/{Drawer => Drawers}/DrawerTemplate.scss (99%) rename client/src/style/components/{Drawer => Drawers}/ViewDetails.scss (77%) diff --git a/client/src/components/Card.js b/client/src/components/Card.js new file mode 100644 index 000000000..e28d3816b --- /dev/null +++ b/client/src/components/Card.js @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function Card({ children }) { + return
{children}
; +} diff --git a/client/src/components/Datatable/DatatableEditable.js b/client/src/components/Datatable/DatatableEditable.js index 911ef15cf..f067ea6af 100644 --- a/client/src/components/Datatable/DatatableEditable.js +++ b/client/src/components/Datatable/DatatableEditable.js @@ -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 ( -
+
diff --git a/client/src/components/Details/index.js b/client/src/components/Details/index.js new file mode 100644 index 000000000..ac6cc0d8e --- /dev/null +++ b/client/src/components/Details/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import className from 'classname'; + +/** + * Details menu. + */ +export function DetailsMenu({ children, vertical = false }) { + return ( +
+ {children} +
+ ); +} + +/** + * Detail item. + */ +export function DetailItem({ label, children }) { + return ( +
+
{label}
+
{children}
+
+ ); +} diff --git a/client/src/components/Drawer/Drawer.js b/client/src/components/Drawer/Drawer.js index 61c736375..09f7b93a5 100644 --- a/client/src/components/Drawer/Drawer.js +++ b/client/src/components/Drawer/Drawer.js @@ -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; diff --git a/client/src/components/index.js b/client/src/components/index.js index fa0d566b7..c2d234be1 100644 --- a/client/src/components/index.js +++ b/client/src/components/index.js @@ -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 }; diff --git a/client/src/containers/Accounting/JournalsLanding/utils.js b/client/src/containers/Accounting/JournalsLanding/utils.js index dd6f3c21f..851eb92d8 100644 --- a/client/src/containers/Accounting/JournalsLanding/utils.js +++ b/client/src/containers/Accounting/JournalsLanding/utils.js @@ -67,4 +67,4 @@ export const useManualJournalsColumns = () => { ], [], ); -}; +}; \ No newline at end of file diff --git a/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js b/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js index 49f138903..8367dc2bd 100644 --- a/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js +++ b/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js @@ -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 (
- - {({ form:{values ,setFieldValue}, field: { value }, meta: { error, touched } }) => ( + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( { setFieldValue('entries', entries); diff --git a/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesHeaderFields.js b/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesHeaderFields.js index 6ec4d25e4..02c329654 100644 --- a/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesHeaderFields.js +++ b/client/src/containers/Accounting/MakeJournal/MakeJournalEntriesHeaderFields.js @@ -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({ {/*------------ Currency -----------*/} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Accounting/MakeJournal/utils.js b/client/src/containers/Accounting/MakeJournal/utils.js index 6a7142f1e..143224e41 100644 --- a/client/src/containers/Accounting/MakeJournal/utils.js +++ b/client/src/containers/Accounting/MakeJournal/utils.js @@ -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) + ); +}; diff --git a/client/src/containers/Alerts/Bills/BillTransactionDeleteAlert.js b/client/src/containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert.js similarity index 91% rename from client/src/containers/Alerts/Bills/BillTransactionDeleteAlert.js rename to client/src/containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert.js index 20c93183c..826e9c925 100644 --- a/client/src/containers/Alerts/Bills/BillTransactionDeleteAlert.js +++ b/client/src/containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert.js @@ -56,7 +56,7 @@ function BillTransactionDeleteAlert({ onConfirm={handleConfirmLandedCostDelete} loading={isLoading} > -

{/* */}

+

); } diff --git a/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostForm.js b/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostForm.js index 3dccf250d..3b6ded70a 100644 --- a/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostForm.js +++ b/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostForm.js @@ -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 ( + 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(), + }), + ), + }); diff --git a/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostFormFields.js b/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostFormFields.js index bb30785ce..4861a65eb 100644 --- a/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostFormFields.js +++ b/client/src/containers/Dialogs/AllocateLandedCostDialog/AllocateLandedCostFormFields.js @@ -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 (
@@ -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 }} /> @@ -112,15 +115,21 @@ export default function AllocateLandedCostFormFields() { { - 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 }} /> @@ -138,13 +147,24 @@ export default function AllocateLandedCostFormFields() { className={'form-group--amount'} inline={true} > - + { + const amount = e.target.value; + const { allocation_method, items } = form.values; + + form.setFieldValue( + 'items', + allocateCostToEntries(amount, allocation_method, items), + ); + }} + /> )} {/*------------ Allocation method -----------*/} - + {({ form, field: { value }, meta: { touched, error } }) => ( { + 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() { )} - + {/*------------ Allocate Landed cost Table -----------*/} diff --git a/client/src/containers/Dialogs/AllocateLandedCostDialog/utils.js b/client/src/containers/Dialogs/AllocateLandedCostDialog/utils.js index fed205e5b..21d5fa076 100644 --- a/client/src/containers/Dialogs/AllocateLandedCostDialog/utils.js +++ b/client/src/containers/Dialogs/AllocateLandedCostDialog/utils.js @@ -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), + })); +} diff --git a/client/src/containers/Drawers/AccountDrawer/AccountDrawerContent.js b/client/src/containers/Drawers/AccountDrawer/AccountDrawerContent.js index aa4bcc436..3bf0bf706 100644 --- a/client/src/containers/Drawers/AccountDrawer/AccountDrawerContent.js +++ b/client/src/containers/Drawers/AccountDrawer/AccountDrawerContent.js @@ -1,4 +1,7 @@ import React from 'react'; + +import 'style/components/Drawers/AccountDrawer.scss'; + import { AccountDrawerProvider } from './AccountDrawerProvider'; import AccountDrawerDetails from './AccountDrawerDetails'; diff --git a/client/src/containers/Drawers/AccountDrawer/AccountDrawerDetails.js b/client/src/containers/Drawers/AccountDrawer/AccountDrawerDetails.js index 230d89beb..30d5a30fb 100644 --- a/client/src/containers/Drawers/AccountDrawer/AccountDrawerDetails.js +++ b/client/src/containers/Drawers/AccountDrawer/AccountDrawerDetails.js @@ -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. */ diff --git a/client/src/containers/Drawers/BillDrawer/BillDrawerAlerts.js b/client/src/containers/Drawers/BillDrawer/BillDrawerAlerts.js index 278671574..bc8ca4660 100644 --- a/client/src/containers/Drawers/BillDrawer/BillDrawerAlerts.js +++ b/client/src/containers/Drawers/BillDrawer/BillDrawerAlerts.js @@ -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 (
- +
); } diff --git a/client/src/containers/Drawers/BillDrawer/BillDrawerContent.js b/client/src/containers/Drawers/BillDrawer/BillDrawerContent.js index ec1579009..a9a49c0ce 100644 --- a/client/src/containers/Drawers/BillDrawer/BillDrawerContent.js +++ b/client/src/containers/Drawers/BillDrawer/BillDrawerContent.js @@ -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'; diff --git a/client/src/containers/Drawers/BillDrawer/BillDrawerDetails.js b/client/src/containers/Drawers/BillDrawer/BillDrawerDetails.js index 28bc4fc3a..bd9de99e1 100644 --- a/client/src/containers/Drawers/BillDrawer/BillDrawerDetails.js +++ b/client/src/containers/Drawers/BillDrawer/BillDrawerDetails.js @@ -4,8 +4,6 @@ import intl from 'react-intl-universal'; import LocatedLandedCostTable from './LocatedLandedCostTable'; -import 'style/components/Drawer/BillDrawer.scss'; - /** * Bill view details. */ diff --git a/client/src/containers/Drawers/BillDrawer/BillDrawerProvider.js b/client/src/containers/Drawers/BillDrawer/BillDrawerProvider.js index 5bd4a8246..47bdd53b1 100644 --- a/client/src/containers/Drawers/BillDrawer/BillDrawerProvider.js +++ b/client/src/containers/Drawers/BillDrawer/BillDrawerProvider.js @@ -18,7 +18,9 @@ function BillDrawerProvider({ billId, ...props }) { //provider. const provider = { transactions, + billId, }; + return ( { - 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 ( - +
+ + +
); } -export default compose(withAlertsActions)(LocatedLandedCostTable); +export default compose( + withAlertsActions, + withDialogActions, + withDrawerActions, +)(LocatedLandedCostTable); diff --git a/client/src/containers/Drawers/BillDrawer/components.js b/client/src/containers/Drawers/BillDrawer/components.js index fb181da03..2a4d29f93 100644 --- a/client/src/containers/Drawers/BillDrawer/components.js +++ b/client/src/containers/Drawers/BillDrawer/components.js @@ -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 ( + + {original.from_transaction_type} → {original.from_transaction_id} + + ); +} + +/** + * 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', + }, + ], + [], + ); } diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js index 37f3bd8cb..fca141626 100644 --- a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js +++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js @@ -1,4 +1,7 @@ import React from 'react'; + +import 'style/components/Drawers/ViewDetails.scss'; + import { ExpenseDrawerProvider } from './ExpenseDrawerProvider'; import ExpenseDrawerDetails from './ExpenseDrawerDetails'; diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js index 763d6304d..8ddbf6674 100644 --- a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js +++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js @@ -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. diff --git a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerContent.js b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerContent.js index c6ff9ccc0..905dd2174 100644 --- a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerContent.js +++ b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerContent.js @@ -1,4 +1,7 @@ import React from 'react'; + +import 'style/components/Drawers/ViewDetails.scss'; + import { ManualJournalDrawerProvider } from './ManualJournalDrawerProvider'; import ManualJournalDrawerDetails from './ManualJournalDrawerDetails'; diff --git a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerDetails.js b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerDetails.js index 804b47c8e..e5129cb5c 100644 --- a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerDetails.js +++ b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerDetails.js @@ -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. */ diff --git a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider.js b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider.js index 966971e19..6b2b531f0 100644 --- a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider.js +++ b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider.js @@ -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(); diff --git a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerTable.js b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerTable.js index e0d59a46f..26798517b 100644 --- a/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerTable.js +++ b/client/src/containers/Drawers/ManualJournalDrawer/ManualJournalDrawerTable.js @@ -69,6 +69,7 @@ export default function ManualJournalDrawerTable({ return (
+

Description: {description} diff --git a/client/src/containers/Drawers/PaperTemplate/PaperTemplate.js b/client/src/containers/Drawers/PaperTemplate/PaperTemplate.js index 9c12fc7fd..d55b83c97 100644 --- a/client/src/containers/Drawers/PaperTemplate/PaperTemplate.js +++ b/client/src/containers/Drawers/PaperTemplate/PaperTemplate.js @@ -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 = { diff --git a/client/src/containers/Drawers/PaymentPaperTemplate/PaymentPaperTemplate.js b/client/src/containers/Drawers/PaymentPaperTemplate/PaymentPaperTemplate.js index b0fbb1ebb..12fb4e403 100644 --- a/client/src/containers/Drawers/PaymentPaperTemplate/PaymentPaperTemplate.js +++ b/client/src/containers/Drawers/PaymentPaperTemplate/PaymentPaperTemplate.js @@ -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, diff --git a/client/src/containers/Entries/ItemsEntriesTable.js b/client/src/containers/Entries/ItemsEntriesTable.js index c4a482012..ce4e0ce9f 100644 --- a/client/src/containers/Entries/ItemsEntriesTable.js +++ b/client/src/containers/Entries/ItemsEntriesTable.js @@ -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( diff --git a/client/src/containers/Entries/components.js b/client/src/containers/Entries/components.js index a9c53596e..59db5385d 100644 --- a/client/src/containers/Entries/components.js +++ b/client/src/containers/Entries/components.js @@ -10,6 +10,7 @@ import { ItemsListCell, PercentFieldCell, NumericInputCell, + CheckBoxFieldCell, } from 'components/DataTableCells'; /** @@ -90,27 +91,18 @@ export function IndexTableCell({ row: { index } }) { return {index + 1}; } -/** - * Landed cost cell. - */ -const LandedCostCell = ({ - row: { index }, - column: { id }, - cell: { value: initialValue }, - data, - payload, -}) => { - return ; -}; - /** * Landed cost header cell. */ const LandedCostHeaderCell = () => { return ( <> - - + + ); }; @@ -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', diff --git a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js index 437b10bb8..abec92e0f 100644 --- a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js +++ b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js @@ -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 ( - + {({ form: { values, setFieldValue }, field: { value }, diff --git a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js index ab1c2cdbc..79ee363be 100644 --- a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js +++ b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js @@ -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 ( - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -118,7 +123,11 @@ export default function ExpenseFormHeader() { )} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Expenses/ExpenseForm/components.js b/client/src/containers/Expenses/ExpenseForm/components.js index 21d71cf20..64f275d00 100644 --- a/client/src/containers/Expenses/ExpenseForm/components.js +++ b/client/src/containers/Expenses/ExpenseForm/components.js @@ -56,8 +56,12 @@ const ActionsCellRenderer = ({ const LandedCostHeaderCell = () => { return ( <> - - + + ); }; @@ -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', diff --git a/client/src/containers/Expenses/ExpenseForm/utils.js b/client/src/containers/Expenses/ExpenseForm/utils.js index d418c42a3..f9d8392c3 100644 --- a/client/src/containers/Expenses/ExpenseForm/utils.js +++ b/client/src/containers/Expenses/ExpenseForm/utils.js @@ -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) + ); +}; diff --git a/client/src/containers/Items/ItemFormBody.js b/client/src/containers/Items/ItemFormBody.js index 9123ebf4d..481e42db9 100644 --- a/client/src/containers/Items/ItemFormBody.js +++ b/client/src/containers/Items/ItemFormBody.js @@ -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 (

@@ -53,7 +62,11 @@ function ItemFormBody({ baseCurrency }) { {/*------------- Selling price ------------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -78,7 +91,12 @@ function ItemFormBody({ baseCurrency }) { {/*------------- Selling account ------------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -107,7 +125,11 @@ function ItemFormBody({ baseCurrency }) { )} - + {({ form: { values }, field, meta: { error, touched } }) => ( } @@ -146,7 +168,11 @@ function ItemFormBody({ baseCurrency }) { {/*------------- Cost price ------------- */} - + {({ field, form, field: { value }, meta: { error, touched } }) => ( } @@ -171,7 +197,12 @@ function ItemFormBody({ baseCurrency }) { {/*------------- Cost account ------------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -200,7 +231,11 @@ function ItemFormBody({ baseCurrency }) { )} - + {({ form: { values }, field, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Items/ItemFormInventorySection.js b/client/src/containers/Items/ItemFormInventorySection.js index c4684fcec..d5a28d7e3 100644 --- a/client/src/containers/Items/ItemFormInventorySection.js +++ b/client/src/containers/Items/ItemFormInventorySection.js @@ -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 }) { {/*------------- Inventory account ------------- */} - + {({ form, field: { value }, meta: { touched, error } }) => ( } diff --git a/client/src/containers/Items/ItemFormPrimarySection.js b/client/src/containers/Items/ItemFormPrimarySection.js index db6ae61d8..118601781 100644 --- a/client/src/containers/Items/ItemFormPrimarySection.js +++ b/client/src/containers/Items/ItemFormPrimarySection.js @@ -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() { {/*----------- Item category ----------*/} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Items/utils.js b/client/src/containers/Items/utils.js index 1a5abe31d..406f54fa1 100644 --- a/client/src/containers/Items/utils.js +++ b/client/src/containers/Items/utils.js @@ -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) + ); +}; diff --git a/client/src/containers/Purchases/Bills/BillForm/BillForm.js b/client/src/containers/Purchases/Bills/BillForm/BillForm.js index 02f6103ca..11dc49733 100644 --- a/client/src/containers/Purchases/Bills/BillForm/BillForm.js +++ b/client/src/containers/Purchases/Bills/BillForm/BillForm.js @@ -48,7 +48,7 @@ function BillForm({ currency_code: baseCurrency, }), }), - [bill], + [bill, baseCurrency], ); // Transform response error to fields. diff --git a/client/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js b/client/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js index 8f5086849..dfa2a5d28 100644 --- a/client/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js +++ b/client/src/containers/Purchases/Bills/BillForm/BillFormHeaderFields.js @@ -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 (
{/* ------- Vendor name ------ */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Purchases/Bills/BillForm/BillItemsEntriesEditor.js b/client/src/containers/Purchases/Bills/BillForm/BillItemsEntriesEditor.js index 25136485e..9ec38558d 100644 --- a/client/src/containers/Purchases/Bills/BillForm/BillItemsEntriesEditor.js +++ b/client/src/containers/Purchases/Bills/BillForm/BillItemsEntriesEditor.js @@ -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 (
- + {({ form: { values, setFieldValue }, field: { value }, @@ -25,6 +35,7 @@ export default function BillFormBody({ defaultBill }) { errors={error} linesNumber={4} currencyCode={values.currency_code} + landedCost={true} /> )} diff --git a/client/src/containers/Purchases/Bills/BillForm/utils.js b/client/src/containers/Purchases/Bills/BillForm/utils.js index dba011cf7..9723b4f97 100644 --- a/client/src/containers/Purchases/Bills/BillForm/utils.js +++ b/client/src/containers/Purchases/Bills/BillForm/utils.js @@ -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) + ); +}; diff --git a/client/src/containers/Purchases/Bills/BillsLanding/components.js b/client/src/containers/Purchases/Bills/BillsLanding/components.js index cf2b6c73a..de7271f8d 100644 --- a/client/src/containers/Purchases/Bills/BillsLanding/components.js +++ b/client/src/containers/Purchases/Bills/BillsLanding/components.js @@ -59,7 +59,7 @@ export function ActionsMenu({ /> } + icon={} text={intl.get('allocate_landed_coast')} onClick={safeCallback(onAllocateLandedCost, original)} /> diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js index 88fdb5652..f22000973 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js @@ -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 (
{/* ------------ Vendor name ------------ */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -157,7 +159,7 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) { small={true} minimal={true} > - ( + ( ) @@ -184,7 +186,11 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) { {/* ------------ Payment account ------------ */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js index 54aa39bca..0467c07c4 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js @@ -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, })); -} \ No newline at end of file +}; + +/** + * 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) + ); +}; diff --git a/client/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeader.js b/client/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeader.js index 91da74c0a..9402d5d75 100644 --- a/client/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeader.js +++ b/client/src/containers/Sales/Estimates/EstimateForm/EstimateFormHeader.js @@ -27,6 +27,7 @@ function EstimateFormHeader({ return (
+ {/* ----------- Customer name ----------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -170,7 +175,9 @@ function EstimateFormHeader({ }} tooltip={true} tooltipProps={{ - content: , + content: ( + + ), position: Position.BOTTOM_LEFT, }} /> diff --git a/client/src/containers/Sales/Estimates/EstimateForm/EstimateItemsEntriesField.js b/client/src/containers/Sales/Estimates/EstimateForm/EstimateItemsEntriesField.js index 82125ccf2..e549f9342 100644 --- a/client/src/containers/Sales/Estimates/EstimateForm/EstimateItemsEntriesField.js +++ b/client/src/containers/Sales/Estimates/EstimateForm/EstimateItemsEntriesField.js @@ -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 (
- + {({ form: { values, setFieldValue }, field: { value }, diff --git a/client/src/containers/Sales/Estimates/EstimateForm/utils.js b/client/src/containers/Sales/Estimates/EstimateForm/utils.js index 42f820356..eba82d104 100644 --- a/client/src/containers/Sales/Estimates/EstimateForm/utils.js +++ b/client/src/containers/Sales/Estimates/EstimateForm/utils.js @@ -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]); -} \ No newline at end of file +}; + +/** + * 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) + ); +}; diff --git a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js index c28ccb1e6..96594a06c 100644 --- a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js +++ b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormHeaderFields.js @@ -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 (
{/* ----------- Customer name ----------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -168,7 +172,9 @@ function InvoiceFormHeaderFields({ }} tooltip={true} tooltipProps={{ - content: , + content: ( + + ), position: Position.BOTTOM_LEFT, }} /> diff --git a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js index 4adf2e44e..afd87d7dc 100644 --- a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js +++ b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js @@ -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 (
- + {({ form: { values, setFieldValue }, field: { value }, diff --git a/client/src/containers/Sales/Invoices/InvoiceForm/utils.js b/client/src/containers/Sales/Invoices/InvoiceForm/utils.js index 6dde8e128..70787c7fa 100644 --- a/client/src/containers/Sales/Invoices/InvoiceForm/utils.js +++ b/client/src/containers/Sales/Invoices/InvoiceForm/utils.js @@ -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]); -}; \ No newline at end of file +}; + +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) + ); +}; diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js index 5fa0f1e11..1bccd3bca 100644 --- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js +++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js @@ -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 (
{/* ------------- Customer name ------------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -247,7 +254,11 @@ function PaymentReceiveHeaderFields({ {/* ------------ Deposit account ------------ */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js index 5513b6939..8309b2fc0 100644 --- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js +++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js @@ -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) + ); +}; diff --git a/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js b/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js index 7db0129d6..d14478e3e 100644 --- a/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js +++ b/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormHeaderFields.js @@ -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 (
{/* ----------- Customer name ----------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } @@ -94,7 +102,11 @@ function ReceiptFormHeader({ {/* ----------- Deposit account ----------- */} - + {({ form, field: { value }, meta: { error, touched } }) => ( } diff --git a/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptItemsEntriesEditor.js b/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptItemsEntriesEditor.js index 581b7f4b0..b678bc7ba 100644 --- a/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptItemsEntriesEditor.js +++ b/client/src/containers/Sales/Receipts/ReceiptForm/ReceiptItemsEntriesEditor.js @@ -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 (
- + {({ form: { values, setFieldValue }, field: { value }, diff --git a/client/src/containers/Sales/Receipts/ReceiptForm/utils.js b/client/src/containers/Sales/Receipts/ReceiptForm/utils.js index 431500601..93cbb75cd 100644 --- a/client/src/containers/Sales/Receipts/ReceiptForm/utils.js +++ b/client/src/containers/Sales/Receipts/ReceiptForm/utils.js @@ -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]); -} \ No newline at end of file +}; + +/** + * 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) + ); +}; diff --git a/client/src/lang/en/index.json b/client/src/lang/en/index.json index 6742cf866..081b54a77 100644 --- a/client/src/lang/en/index.json +++ b/client/src/lang/en/index.json @@ -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?" } \ No newline at end of file diff --git a/client/src/style/components/DataTable/DataTableEditable.scss b/client/src/style/components/DataTable/DataTableEditable.scss index e10c4f78f..703afb089 100644 --- a/client/src/style/components/DataTable/DataTableEditable.scss +++ b/client/src/style/components/DataTable/DataTableEditable.scss @@ -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, diff --git a/client/src/style/components/Details.scss b/client/src/style/components/Details.scss new file mode 100644 index 000000000..d9a2134a1 --- /dev/null +++ b/client/src/style/components/Details.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/client/src/style/components/Drawer.scss b/client/src/style/components/Drawer.scss new file mode 100644 index 000000000..8d4fc9f4c --- /dev/null +++ b/client/src/style/components/Drawer.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/client/src/style/components/Drawer/AccountDrawer.scss b/client/src/style/components/Drawers/AccountDrawer.scss similarity index 72% rename from client/src/style/components/Drawer/AccountDrawer.scss rename to client/src/style/components/Drawers/AccountDrawer.scss index 318c66a44..2a90ded17 100644 --- a/client/src/style/components/Drawer/AccountDrawer.scss +++ b/client/src/style/components/Drawers/AccountDrawer.scss @@ -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; - } - } } \ No newline at end of file diff --git a/client/src/style/components/Drawer/BillDrawer.scss b/client/src/style/components/Drawers/BillDrawer.scss similarity index 60% rename from client/src/style/components/Drawer/BillDrawer.scss rename to client/src/style/components/Drawers/BillDrawer.scss index d3df06a54..3179bda99 100644 --- a/client/src/style/components/Drawer/BillDrawer.scss +++ b/client/src/style/components/Drawers/BillDrawer.scss @@ -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; - } - } -} +} \ No newline at end of file diff --git a/client/src/style/components/Drawer/DrawerTemplate.scss b/client/src/style/components/Drawers/DrawerTemplate.scss similarity index 99% rename from client/src/style/components/Drawer/DrawerTemplate.scss rename to client/src/style/components/Drawers/DrawerTemplate.scss index 4ad14b775..60e63bc8c 100644 --- a/client/src/style/components/Drawer/DrawerTemplate.scss +++ b/client/src/style/components/Drawers/DrawerTemplate.scss @@ -122,6 +122,7 @@ top: 0; overflow: auto; height: 100%; + .bp3-drawer-header .bp3-heading { overflow: hidden; text-overflow: ellipsis; diff --git a/client/src/style/components/Drawer/ViewDetails.scss b/client/src/style/components/Drawers/ViewDetails.scss similarity index 77% rename from client/src/style/components/Drawer/ViewDetails.scss rename to client/src/style/components/Drawers/ViewDetails.scss index 2a71ec9d8..f0d982727 100644 --- a/client/src/style/components/Drawer/ViewDetails.scss +++ b/client/src/style/components/Drawers/ViewDetails.scss @@ -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; - } - } -} diff --git a/client/src/style/pages/Bills/PageForm.scss b/client/src/style/pages/Bills/PageForm.scss index edecac102..df8a23c28 100644 --- a/client/src/style/pages/Bills/PageForm.scss +++ b/client/src/style/pages/Bills/PageForm.scss @@ -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; diff --git a/client/src/style/pages/Expense/PageForm.scss b/client/src/style/pages/Expense/PageForm.scss index 6a93637e5..102a3819e 100644 --- a/client/src/style/pages/Expense/PageForm.scss +++ b/client/src/style/pages/Expense/PageForm.scss @@ -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%; } diff --git a/client/src/style/pages/fonts.scss b/client/src/style/pages/fonts.scss index 76e0d759f..8f5bb71b8 100644 --- a/client/src/style/pages/fonts.scss +++ b/client/src/style/pages/fonts.scss @@ -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'; diff --git a/client/src/style/variables.scss b/client/src/style/variables.scss index 63c0400ee..02561ebe9 100644 --- a/client/src/style/variables.scss +++ b/client/src/style/variables.scss @@ -16,7 +16,7 @@ $menu-item-color-active: $light-gray3; $breadcrumbs-collapsed-icon: url("data:image/svg+xml,"); $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; diff --git a/client/src/utils.js b/client/src/utils.js index a6e3d07e0..32dadaaec 100644 --- a/client/src/utils.js +++ b/client/src/utils.js @@ -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(); \ No newline at end of file +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 + ); +}; diff --git a/server/src/api/controllers/FinancialStatements/CashFlow/CashFlow.ts b/server/src/api/controllers/FinancialStatements/CashFlow/CashFlow.ts index 87235b609..aa0943833 100644 --- a/server/src/api/controllers/FinancialStatements/CashFlow/CashFlow.ts +++ b/server/src/api/controllers/FinancialStatements/CashFlow/CashFlow.ts @@ -71,7 +71,6 @@ export default class CashFlowController extends BaseFinancialReportController { /** * Transformes the report statement to table rows. * @param {ITransactionsByVendorsStatement} statement - - * */ private transformToTableRows(cashFlowDOO: ICashFlowStatementDOO, tenantId: number) { const i18n = this.tenancy.i18n(tenantId); diff --git a/server/src/api/controllers/FinancialStatements/VendorBalanceSummary/index.ts b/server/src/api/controllers/FinancialStatements/VendorBalanceSummary/index.ts index 849a64eca..41b2e88c1 100644 --- a/server/src/api/controllers/FinancialStatements/VendorBalanceSummary/index.ts +++ b/server/src/api/controllers/FinancialStatements/VendorBalanceSummary/index.ts @@ -45,7 +45,7 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR * Transformes the report statement to table rows. * @param {IVendorBalanceSummaryStatement} statement - */ - transformToTableRows({ data }: IVendorBalanceSummaryStatement) { + private transformToTableRows({ data }: IVendorBalanceSummaryStatement) { return { table: { data: this.vendorBalanceSummaryTableRows.tableRowsTransformer(data), @@ -57,7 +57,10 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR * Transformes the report statement to raw json. * @param {IVendorBalanceSummaryStatement} statement - */ - transformToJsonResponse({ data, columns }: IVendorBalanceSummaryStatement) { + private transformToJsonResponse({ + data, + columns, + }: IVendorBalanceSummaryStatement) { return { data: this.transfromToResponse(data), columns: this.transfromToResponse(columns), @@ -76,10 +79,11 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR const filter = this.matchedQueryData(req); try { - const vendorBalanceSummary = await this.vendorBalanceSummaryService.vendorBalanceSummary( - tenantId, - filter - ); + const vendorBalanceSummary = + await this.vendorBalanceSummaryService.vendorBalanceSummary( + tenantId, + filter + ); const accept = this.accepts(req); const acceptType = accept.types(['json', 'application/json+table']); diff --git a/server/src/api/controllers/Purchases/LandedCost.ts b/server/src/api/controllers/Purchases/LandedCost.ts index 5471be78c..bf149c2ff 100644 --- a/server/src/api/controllers/Purchases/LandedCost.ts +++ b/server/src/api/controllers/Purchases/LandedCost.ts @@ -192,7 +192,10 @@ export default class BillAllocateLandedCost extends BaseController { billId ); - return res.status(200).send({ billId, transactions }); + return res.status(200).send({ + billId, + transactions: this.transfromToResponse(transactions) + }); } catch (error) { next(error); } diff --git a/server/src/services/Purchases/LandedCost/LandedCostListing.ts b/server/src/services/Purchases/LandedCost/LandedCostListing.ts index e476b9f93..afa02a5aa 100644 --- a/server/src/services/Purchases/LandedCost/LandedCostListing.ts +++ b/server/src/services/Purchases/LandedCost/LandedCostListing.ts @@ -1,5 +1,5 @@ import { Inject, Service } from 'typedi'; -import { ref } from 'objection'; +import { ref, transaction } from 'objection'; import { ILandedCostTransactionsQueryDTO, ILandedCostTransaction, @@ -8,6 +8,7 @@ import { import TransactionLandedCost from './TransctionLandedCost'; import BillsService from '../Bills'; import HasTenancyService from 'services/Tenancy/TenancyService'; +import { formatNumber } from 'utils'; @Service() export default class LandedCostListing { @@ -71,8 +72,15 @@ export default class LandedCostListing { const landedCostTransactions = await BillLandedCost.query() .where('bill_id', billId) - .withGraphFetched('allocateEntries'); + .withGraphFetched('allocateEntries') + .withGraphFetched('bill'); - return landedCostTransactions; + return landedCostTransactions.map((transaction) => ({ + ...transaction.toJSON(), + formattedAmount: formatNumber( + transaction.amount, + transaction.bill.currencyCode + ), + })); }; }