mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50: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 { DataTable, If } from 'components';
|
||||||
import 'style/components/DataTable/DataTableEditable.scss';
|
import 'style/components/DataTable/DataTableEditable.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editable datatable.
|
||||||
|
*/
|
||||||
export default function DatatableEditable({
|
export default function DatatableEditable({
|
||||||
totalRow = false,
|
totalRow = false,
|
||||||
actions,
|
actions,
|
||||||
|
name,
|
||||||
className,
|
className,
|
||||||
...tableProps
|
...tableProps
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.DATATABLE_EDITOR, className)}>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
CLASSES.DATATABLE_EDITOR,
|
||||||
|
{
|
||||||
|
[`${CLASSES.DATATABLE_EDITOR}--${name}`]: name,
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
<DataTable {...tableProps} />
|
<DataTable {...tableProps} />
|
||||||
|
|
||||||
<If condition={actions}>
|
<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 React from 'react';
|
||||||
import { Position, Drawer } from '@blueprintjs/core';
|
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';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drawer component.
|
||||||
|
*/
|
||||||
function DrawerComponent(props) {
|
function DrawerComponent(props) {
|
||||||
const { name, children, onClose, closeDrawer } = props;
|
const { name, children, onClose, closeDrawer } = props;
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import Postbox from './Postbox';
|
|||||||
import AccountsSuggestField from './AccountsSuggestField';
|
import AccountsSuggestField from './AccountsSuggestField';
|
||||||
import MaterialProgressBar from './MaterialProgressBar';
|
import MaterialProgressBar from './MaterialProgressBar';
|
||||||
import { MoneyFieldCell } from './DataTableCells';
|
import { MoneyFieldCell } from './DataTableCells';
|
||||||
|
import Card from './Card';
|
||||||
|
|
||||||
import { ItemsMultiSelect } from './Items';
|
import { ItemsMultiSelect } from './Items';
|
||||||
|
|
||||||
@@ -127,5 +128,6 @@ export {
|
|||||||
AccountsSuggestField,
|
AccountsSuggestField,
|
||||||
MaterialProgressBar,
|
MaterialProgressBar,
|
||||||
MoneyFieldCell,
|
MoneyFieldCell,
|
||||||
ItemsMultiSelect
|
ItemsMultiSelect,
|
||||||
|
Card
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,16 +3,28 @@ import { FastField } from 'formik';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
|
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.
|
* Make journal entries field.
|
||||||
*/
|
*/
|
||||||
export default function MakeJournalEntriesField() {
|
export default function MakeJournalEntriesField() {
|
||||||
|
const { accounts, contacts } = useMakeJournalFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<FastField name={'entries'}>
|
<FastField
|
||||||
{({ form:{values ,setFieldValue}, field: { value }, meta: { error, touched } }) => (
|
name={'entries'}
|
||||||
|
contacts={contacts}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={entriesFieldShouldUpdate}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
form: { values, setFieldValue },
|
||||||
|
field: { value },
|
||||||
|
meta: { error, touched },
|
||||||
|
}) => (
|
||||||
<MakeJournalEntriesTable
|
<MakeJournalEntriesTable
|
||||||
onChange={(entries) => {
|
onChange={(entries) => {
|
||||||
setFieldValue('entries', entries);
|
setFieldValue('entries', entries);
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ import {
|
|||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import { useObserveJournalNoSettings } from './utils';
|
import {
|
||||||
|
currenciesFieldShouldUpdate,
|
||||||
|
useObserveJournalNoSettings,
|
||||||
|
} from './utils';
|
||||||
/**
|
/**
|
||||||
* Make journal entries header.
|
* Make journal entries header.
|
||||||
*/
|
*/
|
||||||
@@ -182,7 +185,11 @@ function MakeJournalEntriesHeader({
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------ Currency -----------*/}
|
{/*------------ Currency -----------*/}
|
||||||
<FastField name={'currency_code'}>
|
<FastField
|
||||||
|
name={'currency_code'}
|
||||||
|
currencies={currencies}
|
||||||
|
shouldUpdate={currenciesFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'currency'} />}
|
label={<T id={'currency'} />}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Intent } from '@blueprintjs/core';
|
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 moment from 'moment';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
transactionNumber,
|
transactionNumber,
|
||||||
updateTableRow,
|
updateTableRow,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
transformToForm,
|
transformToForm,
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
@@ -123,17 +123,17 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
|
|||||||
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
||||||
}
|
}
|
||||||
if ((error = getError(ERROR.ENTRIES_SHOULD_ASSIGN_WITH_CONTACT))) {
|
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(
|
toastMessages.push(
|
||||||
intl.get('receivable_accounts_should_assign_with_customers'),
|
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(
|
toastMessages.push(
|
||||||
intl.get('payable_accounts_should_assign_with_vendors'),
|
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');
|
setEntriesErrors(indexes, 'contact_id', 'error');
|
||||||
}
|
}
|
||||||
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
||||||
@@ -163,3 +163,24 @@ export const useObserveJournalNoSettings = (prefix, nextNumber) => {
|
|||||||
setFieldValue('journal_number', journalNo);
|
setFieldValue('journal_number', journalNo);
|
||||||
}, [setFieldValue, prefix, nextNumber]);
|
}, [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}
|
onConfirm={handleConfirmLandedCostDelete}
|
||||||
loading={isLoading}
|
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>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Formik } from 'formik';
|
|||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
|
||||||
import 'style/pages/AllocateLandedCost/AllocateLandedCostForm.scss';
|
import 'style/pages/AllocateLandedCost/AllocateLandedCostForm.scss';
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ function AllocateLandedCostForm({
|
|||||||
cost: '',
|
cost: '',
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
const amount = sumBy(initialValues.items, 'amount');
|
||||||
|
|
||||||
// Handle form submit.
|
// Handle form submit.
|
||||||
const handleFormSubmit = (values, { setSubmitting }) => {
|
const handleFormSubmit = (values, { setSubmitting }) => {
|
||||||
@@ -84,9 +86,12 @@ function AllocateLandedCostForm({
|
|||||||
createLandedCostMutate([billId, form]).then(onSuccess).catch(onError);
|
createLandedCostMutate([billId, form]).then(onSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Computed validation schema.
|
||||||
|
const validationSchema = AllocateLandedCostFormSchema(amount);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={AllocateLandedCostFormSchema}
|
validationSchema={validationSchema}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
component={AllocateLandedCostFormContent}
|
component={AllocateLandedCostFormContent}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
const Schema = Yup.object().shape({
|
export const AllocateLandedCostFormSchema = (minAmount) =>
|
||||||
|
Yup.object().shape({
|
||||||
transaction_type: Yup.string().label(intl.get('transaction_type')),
|
transaction_type: Yup.string().label(intl.get('transaction_type')),
|
||||||
transaction_date: Yup.date().label(intl.get('transaction_date')),
|
transaction_date: Yup.date().label(intl.get('transaction_date')),
|
||||||
transaction_id: Yup.string().label(intl.get('transaction_number')),
|
transaction_id: Yup.string().label(intl.get('transaction_number')),
|
||||||
transaction_entry_id: Yup.string().label(intl.get('transaction_line')),
|
transaction_entry_id: Yup.string().label(intl.get('transaction_line')),
|
||||||
amount: Yup.number().label(intl.get('amount')),
|
amount: Yup.number().max(minAmount).label(intl.get('amount')),
|
||||||
allocation_method: Yup.string().trim(),
|
allocation_method: Yup.string().trim(),
|
||||||
items: Yup.array().of(
|
items: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
@@ -14,6 +15,4 @@ const Schema = Yup.object().shape({
|
|||||||
cost: Yup.number().nullable(),
|
cost: Yup.number().nullable(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AllocateLandedCostFormSchema = Schema;
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import allocateLandedCostType from 'common/allocateLandedCostType';
|
|||||||
import { useLandedCostTransaction } from 'hooks/query';
|
import { useLandedCostTransaction } from 'hooks/query';
|
||||||
|
|
||||||
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
|
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
|
||||||
import { getEntriesByTransactionId } from './utils';
|
import { getEntriesByTransactionId, allocateCostToEntries } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate landed cost form fields.
|
* Allocate landed cost form fields.
|
||||||
@@ -30,10 +30,10 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
} = useLandedCostTransaction(values.transaction_type);
|
} = useLandedCostTransaction(values.transaction_type);
|
||||||
|
|
||||||
// Retrieve entries of the given transaction id.
|
// Retrieve entries of the given transaction id.
|
||||||
const transactionEntries = React.useMemo(() => getEntriesByTransactionId(
|
const transactionEntries = React.useMemo(
|
||||||
transactions,
|
() => getEntriesByTransactionId(transactions, values.transaction_id),
|
||||||
values.transaction_id,
|
[transactions, values.transaction_id],
|
||||||
), [transactions, values.transaction_id]);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={Classes.DIALOG_BODY}>
|
<div className={Classes.DIALOG_BODY}>
|
||||||
@@ -56,6 +56,8 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
items={allocateLandedCostType}
|
items={allocateLandedCostType}
|
||||||
onItemSelect={(type) => {
|
onItemSelect={(type) => {
|
||||||
setFieldValue('transaction_type', type.value);
|
setFieldValue('transaction_type', type.value);
|
||||||
|
setFieldValue('transaction_id', '');
|
||||||
|
setFieldValue('transaction_entry_id', '');
|
||||||
}}
|
}}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
selectedItem={value}
|
selectedItem={value}
|
||||||
@@ -82,13 +84,14 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
items={transactions}
|
items={transactions}
|
||||||
onItemSelect={({ id }) => {
|
onItemSelect={({ id }) => {
|
||||||
form.setFieldValue('transaction_id', id);
|
form.setFieldValue('transaction_id', id);
|
||||||
|
form.setFieldValue('transaction_entry_id', '');
|
||||||
}}
|
}}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
selectedItem={value}
|
selectedItem={value}
|
||||||
selectedItemProp={'id'}
|
selectedItemProp={'id'}
|
||||||
textProp={'name'}
|
textProp={'name'}
|
||||||
labelProp={'id'}
|
labelProp={'id'}
|
||||||
defaultText={intl.get('select_transaction')}
|
defaultText={intl.get('Select transaction')}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -112,15 +115,21 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
<ListSelect
|
<ListSelect
|
||||||
items={transactionEntries}
|
items={transactionEntries}
|
||||||
onItemSelect={({ id, amount }) => {
|
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('transaction_entry_id', id);
|
||||||
|
|
||||||
|
form.setFieldValue(
|
||||||
|
'items',
|
||||||
|
allocateCostToEntries(amount, allocation_method, items),
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
selectedItem={value}
|
selectedItem={value}
|
||||||
selectedItemProp={'id'}
|
selectedItemProp={'id'}
|
||||||
textProp={'name'}
|
textProp={'name'}
|
||||||
labelProp={'id'}
|
defaultText={intl.get('Select transaction entry')}
|
||||||
defaultText={intl.get('select_transaction')}
|
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -138,13 +147,24 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
className={'form-group--amount'}
|
className={'form-group--amount'}
|
||||||
inline={true}
|
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>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------ Allocation method -----------*/}
|
{/*------------ Allocation method -----------*/}
|
||||||
<FastField name={'allocation_method'}>
|
<Field name={'allocation_method'}>
|
||||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
medium={true}
|
medium={true}
|
||||||
@@ -157,7 +177,13 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
>
|
>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
onChange={handleStringChange((_value) => {
|
onChange={handleStringChange((_value) => {
|
||||||
|
const { amount, items, allocation_method } = form.values;
|
||||||
|
|
||||||
form.setFieldValue('allocation_method', _value);
|
form.setFieldValue('allocation_method', _value);
|
||||||
|
form.setFieldValue(
|
||||||
|
'items',
|
||||||
|
allocateCostToEntries(amount, allocation_method, items),
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
selectedValue={value}
|
selectedValue={value}
|
||||||
inline={true}
|
inline={true}
|
||||||
@@ -167,7 +193,7 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</Field>
|
||||||
|
|
||||||
{/*------------ Allocate Landed cost Table -----------*/}
|
{/*------------ Allocate Landed cost Table -----------*/}
|
||||||
<AllocateLandedCostFormBody />
|
<AllocateLandedCostFormBody />
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { sumBy, round } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
/**
|
/**
|
||||||
* Retrieve transaction entries of the given transaction id.
|
* 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);
|
const transaction = transactions.find((trans) => trans.id === id);
|
||||||
return transaction ? transaction.entries : [];
|
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 React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/AccountDrawer.scss';
|
||||||
|
|
||||||
import { AccountDrawerProvider } from './AccountDrawerProvider';
|
import { AccountDrawerProvider } from './AccountDrawerProvider';
|
||||||
import AccountDrawerDetails from './AccountDrawerDetails';
|
import AccountDrawerDetails from './AccountDrawerDetails';
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import AccountDrawerHeader from './AccountDrawerHeader';
|
|||||||
import AccountDrawerTable from './AccountDrawerTable';
|
import AccountDrawerTable from './AccountDrawerTable';
|
||||||
import { useAccountDrawerContext } from './AccountDrawerProvider';
|
import { useAccountDrawerContext } from './AccountDrawerProvider';
|
||||||
|
|
||||||
import 'style/components/Drawer/AccountDrawer.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account view details.
|
* Account view details.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BillTransactionDeleteAlert from 'containers/Alerts/Bills/BillTransactionDeleteAlert';
|
import BillLocatedLandedCostDeleteAlert from 'containers/Alerts/Bills/BillLocatedLandedCostDeleteAlert';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bill drawer alert.
|
* Bill drawer alert.
|
||||||
@@ -7,7 +7,7 @@ import BillTransactionDeleteAlert from 'containers/Alerts/Bills/BillTransactionD
|
|||||||
export default function BillDrawerAlerts() {
|
export default function BillDrawerAlerts() {
|
||||||
return (
|
return (
|
||||||
<div class="bills-alerts">
|
<div class="bills-alerts">
|
||||||
<BillTransactionDeleteAlert name="transaction-delete" />
|
<BillLocatedLandedCostDeleteAlert name="bill-located-cost-delete" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/BillDrawer.scss';
|
||||||
|
|
||||||
import { BillDrawerProvider } from './BillDrawerProvider';
|
import { BillDrawerProvider } from './BillDrawerProvider';
|
||||||
import BillDrawerDetails from './BillDrawerDetails';
|
import BillDrawerDetails from './BillDrawerDetails';
|
||||||
import BillDrawerAlerts from './BillDrawerAlerts';
|
import BillDrawerAlerts from './BillDrawerAlerts';
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import intl from 'react-intl-universal';
|
|||||||
|
|
||||||
import LocatedLandedCostTable from './LocatedLandedCostTable';
|
import LocatedLandedCostTable from './LocatedLandedCostTable';
|
||||||
|
|
||||||
import 'style/components/Drawer/BillDrawer.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bill view details.
|
* Bill view details.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ function BillDrawerProvider({ billId, ...props }) {
|
|||||||
//provider.
|
//provider.
|
||||||
const provider = {
|
const provider = {
|
||||||
transactions,
|
transactions,
|
||||||
|
billId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider loading={isLandedCostLoading}>
|
<DashboardInsider loading={isLandedCostLoading}>
|
||||||
<DrawerHeaderContent
|
<DrawerHeaderContent
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import React from 'react';
|
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 { useLocatedLandedCostColumns, ActionsMenu } from './components';
|
||||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||||
|
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Located landed cost table.
|
* Located landed cost table.
|
||||||
@@ -13,25 +19,73 @@ import { compose } from 'utils';
|
|||||||
function LocatedLandedCostTable({
|
function LocatedLandedCostTable({
|
||||||
// #withAlertsActions
|
// #withAlertsActions
|
||||||
openAlert,
|
openAlert,
|
||||||
|
|
||||||
|
// #withDialogActions
|
||||||
|
openDialog,
|
||||||
|
|
||||||
|
// #withDrawerActions
|
||||||
|
openDrawer,
|
||||||
}) {
|
}) {
|
||||||
const columns = useLocatedLandedCostColumns();
|
const columns = useLocatedLandedCostColumns();
|
||||||
const { transactions } = useBillDrawerContext();
|
const { transactions, billId } = useBillDrawerContext();
|
||||||
|
|
||||||
// Handle the transaction delete action.
|
// Handle the transaction delete action.
|
||||||
const handleDeleteTransaction = ({ id }) => {
|
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 (
|
return (
|
||||||
|
<div>
|
||||||
|
<DashboardActionsBar>
|
||||||
|
<NavbarGroup>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon="receipt-24" />}
|
||||||
|
text={'Allocate landed cost'}
|
||||||
|
onClick={handleAllocateCostClick}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</DashboardActionsBar>
|
||||||
|
|
||||||
|
<Card>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={transactions}
|
data={transactions}
|
||||||
ContextMenu={ActionsMenu}
|
ContextMenu={ActionsMenu}
|
||||||
payload={{
|
payload={{
|
||||||
onDelete: handleDeleteTransaction,
|
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 { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||||
import { safeCallback } from 'utils';
|
import { safeCallback } from 'utils';
|
||||||
import { Icon } from 'components';
|
import { Icon } from 'components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actions menu.
|
* Actions menu.
|
||||||
*/
|
*/
|
||||||
@@ -20,22 +19,57 @@ export function ActionsMenu({ row: { original }, payload: { onDelete } }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
export function useLocatedLandedCostColumns() {
|
||||||
return React.useMemo(() => [
|
return React.useMemo(
|
||||||
|
() => [
|
||||||
{
|
{
|
||||||
Header: intl.get('name'),
|
Header: intl.get('name'),
|
||||||
accessor: 'description',
|
accessor: 'description',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
className: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: intl.get('amount'),
|
Header: intl.get('amount'),
|
||||||
accessor: 'amount',
|
accessor: 'formatted_amount',
|
||||||
width: 100,
|
width: 100,
|
||||||
|
className: 'amount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'from_transaction',
|
||||||
|
Header: intl.get('From transaction'),
|
||||||
|
Cell: FromTransactionCell,
|
||||||
|
width: 100,
|
||||||
|
className: 'from-transaction',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: intl.get('allocation_method'),
|
Header: intl.get('allocation_method'),
|
||||||
accessor: 'allocation_method',
|
accessor: 'allocation_method_formatted',
|
||||||
width: 100,
|
width: 100,
|
||||||
|
className: 'allocation-method',
|
||||||
},
|
},
|
||||||
]);
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/ViewDetails.scss';
|
||||||
|
|
||||||
import { ExpenseDrawerProvider } from './ExpenseDrawerProvider';
|
import { ExpenseDrawerProvider } from './ExpenseDrawerProvider';
|
||||||
import ExpenseDrawerDetails from './ExpenseDrawerDetails';
|
import ExpenseDrawerDetails from './ExpenseDrawerDetails';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import ExpenseDrawerHeader from './ExpenseDrawerHeader';
|
|||||||
import ExpenseDrawerTable from './ExpenseDrawerTable';
|
import ExpenseDrawerTable from './ExpenseDrawerTable';
|
||||||
import ExpenseDrawerFooter from './ExpenseDrawerFooter';
|
import ExpenseDrawerFooter from './ExpenseDrawerFooter';
|
||||||
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
|
||||||
import 'style/components/Drawer/ViewDetails.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expense view details.
|
* Expense view details.
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/ViewDetails.scss';
|
||||||
|
|
||||||
import { ManualJournalDrawerProvider } from './ManualJournalDrawerProvider';
|
import { ManualJournalDrawerProvider } from './ManualJournalDrawerProvider';
|
||||||
import ManualJournalDrawerDetails from './ManualJournalDrawerDetails';
|
import ManualJournalDrawerDetails from './ManualJournalDrawerDetails';
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import ManualJournalDrawerFooter from './ManualJournalDrawerFooter';
|
|||||||
|
|
||||||
import { useManualJournalDrawerContext } from 'containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider';
|
import { useManualJournalDrawerContext } from 'containers/Drawers/ManualJournalDrawer/ManualJournalDrawerProvider';
|
||||||
|
|
||||||
import 'style/components/Drawer/ViewDetails.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manual journal view details.
|
* Manual journal view details.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
import { useJournal } from 'hooks/query';
|
import { useJournal } from 'hooks/query';
|
||||||
import { DashboardInsider, DrawerHeaderContent } from 'components';
|
import { DashboardInsider, DrawerHeaderContent } from 'components';
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
|
|
||||||
const ManualJournalDrawerContext = React.createContext();
|
const ManualJournalDrawerContext = React.createContext();
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export default function ManualJournalDrawerTable({
|
|||||||
return (
|
return (
|
||||||
<div className="journal-drawer__content--table">
|
<div className="journal-drawer__content--table">
|
||||||
<DataTable columns={columns} data={entries} />
|
<DataTable columns={columns} data={entries} />
|
||||||
|
|
||||||
<If condition={description}>
|
<If condition={description}>
|
||||||
<p className={'desc'}>
|
<p className={'desc'}>
|
||||||
<b>Description</b>: {description}
|
<b>Description</b>: {description}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/DrawerTemplate.scss';
|
||||||
|
|
||||||
import PaperTemplateHeader from './PaperTemplateHeader';
|
import PaperTemplateHeader from './PaperTemplateHeader';
|
||||||
import PaperTemplateTable from './PaperTemplateTable';
|
import PaperTemplateTable from './PaperTemplateTable';
|
||||||
import PaperTemplateFooter from './PaperTemplateFooter';
|
import PaperTemplateFooter from './PaperTemplateFooter';
|
||||||
import { updateItemsEntriesTotal } from 'containers/Entries/utils';
|
import { updateItemsEntriesTotal } from 'containers/Entries/utils';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import 'style/components/Drawer/DrawerTemplate.scss';
|
|
||||||
|
|
||||||
function PaperTemplate({ labels: propLabels, paperData, entries }) {
|
function PaperTemplate({ labels: propLabels, paperData, entries }) {
|
||||||
const labels = {
|
const labels = {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/Drawers/DrawerTemplate.scss';
|
||||||
|
|
||||||
import PaymentPaperTemplateHeader from './PaymentPaperTemplateHeader';
|
import PaymentPaperTemplateHeader from './PaymentPaperTemplateHeader';
|
||||||
import PaymentPaperTemplateTable from './PaymentPaperTemplateTable';
|
import PaymentPaperTemplateTable from './PaymentPaperTemplateTable';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import 'style/components/Drawer/DrawerTemplate.scss';
|
|
||||||
|
|
||||||
export default function PaymentPaperTemplate({
|
export default function PaymentPaperTemplate({
|
||||||
labels: propLabels,
|
labels: propLabels,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ function ItemsEntriesTable({
|
|||||||
linesNumber,
|
linesNumber,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
itemType, // sellable or purchasable
|
itemType, // sellable or purchasable
|
||||||
|
landedCost = false
|
||||||
}) {
|
}) {
|
||||||
const [rows, setRows] = React.useState(initialEntries);
|
const [rows, setRows] = React.useState(initialEntries);
|
||||||
const [rowItem, setRowItem] = React.useState(null);
|
const [rowItem, setRowItem] = React.useState(null);
|
||||||
@@ -94,7 +95,7 @@ function ItemsEntriesTable({
|
|||||||
}, [entries, rows]);
|
}, [entries, rows]);
|
||||||
|
|
||||||
// Editiable items entries columns.
|
// Editiable items entries columns.
|
||||||
const columns = useEditableItemsEntriesColumns();
|
const columns = useEditableItemsEntriesColumns({ landedCost });
|
||||||
|
|
||||||
// Handles the editor data update.
|
// Handles the editor data update.
|
||||||
const handleUpdateData = useCallback(
|
const handleUpdateData = useCallback(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ItemsListCell,
|
ItemsListCell,
|
||||||
PercentFieldCell,
|
PercentFieldCell,
|
||||||
NumericInputCell,
|
NumericInputCell,
|
||||||
|
CheckBoxFieldCell,
|
||||||
} from 'components/DataTableCells';
|
} from 'components/DataTableCells';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,27 +91,18 @@ export function IndexTableCell({ row: { index } }) {
|
|||||||
return <span>{index + 1}</span>;
|
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.
|
* Landed cost header cell.
|
||||||
*/
|
*/
|
||||||
const LandedCostHeaderCell = () => {
|
const LandedCostHeaderCell = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<T id={'cost'} />
|
<T id={'Landed'} />
|
||||||
<Hint content={''} />
|
<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.
|
* Retrieve editable items entries columns.
|
||||||
*/
|
*/
|
||||||
export function useEditableItemsEntriesColumns() {
|
export function useEditableItemsEntriesColumns({ landedCost }) {
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -182,14 +174,19 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
width: 100,
|
width: 100,
|
||||||
className: 'total',
|
className: 'total',
|
||||||
},
|
},
|
||||||
|
...(landedCost
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
Header: '',
|
Header: LandedCostHeaderCell,
|
||||||
accessor: 'landed_cost',
|
accessor: 'landed_cost',
|
||||||
Cell: LandedCostCell,
|
Cell: CheckBoxFieldCell,
|
||||||
width: 70,
|
width: 100,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
disableResizing: true,
|
disableResizing: true,
|
||||||
|
className: 'landed-cost',
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
Header: '',
|
Header: '',
|
||||||
accessor: 'action',
|
accessor: 'action',
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
|
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
|
||||||
import { defaultExpenseEntry } from './utils';
|
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||||
|
import { defaultExpenseEntry, accountsFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expense form entries field.
|
* Expense form entries field.
|
||||||
*/
|
*/
|
||||||
export default function ExpenseFormEntriesField({ linesNumber = 4 }) {
|
export default function ExpenseFormEntriesField({ linesNumber = 4 }) {
|
||||||
|
// Expense form context.
|
||||||
|
const { accounts } = useExpenseFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FastField name={'categories'}>
|
<FastField
|
||||||
|
name={'categories'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({
|
{({
|
||||||
form: { values, setFieldValue },
|
form: { values, setFieldValue },
|
||||||
field: { value },
|
field: { value },
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ export default function ExpenseFormEntriesTable({
|
|||||||
error,
|
error,
|
||||||
onChange,
|
onChange,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
|
landedCost = true,
|
||||||
}) {
|
}) {
|
||||||
// Expense form context.
|
// Expense form context.
|
||||||
const { accounts } = useExpenseFormContext();
|
const { accounts } = useExpenseFormContext();
|
||||||
|
|
||||||
// Memorized data table columns.
|
// Memorized data table columns.
|
||||||
const columns = useExpenseFormTableColumns();
|
const columns = useExpenseFormTableColumns({ landedCost });
|
||||||
|
|
||||||
// Handles update datatable data.
|
// Handles update datatable data.
|
||||||
const handleUpdateData = useCallback(
|
const handleUpdateData = useCallback(
|
||||||
@@ -61,6 +62,7 @@ export default function ExpenseFormEntriesTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
|
name={'expense-form'}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={entries}
|
data={entries}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
inputIntent,
|
inputIntent,
|
||||||
handleDateChange,
|
handleDateChange,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
import { customersFieldShouldUpdate, accountsFieldShouldUpdate } from './utils';
|
||||||
import {
|
import {
|
||||||
CurrencySelectList,
|
CurrencySelectList,
|
||||||
ContactSelecetList,
|
ContactSelecetList,
|
||||||
@@ -51,7 +52,11 @@ export default function ExpenseFormHeader() {
|
|||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'payment_account_id'}>
|
<FastField
|
||||||
|
name={'payment_account_id'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'payment_account'} />}
|
label={<T id={'payment_account'} />}
|
||||||
@@ -118,7 +123,11 @@ export default function ExpenseFormHeader() {
|
|||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'customer_id'}>
|
<FastField
|
||||||
|
name={'customer_id'}
|
||||||
|
customers={customers}
|
||||||
|
shouldUpdate={customersFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer'} />}
|
label={<T id={'customer'} />}
|
||||||
|
|||||||
@@ -56,8 +56,12 @@ const ActionsCellRenderer = ({
|
|||||||
const LandedCostHeaderCell = () => {
|
const LandedCostHeaderCell = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<T id={'cost'} />
|
<T id={'Landed'} />
|
||||||
<Hint content={''} />
|
<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.
|
* Retrieve expense form table entries columns.
|
||||||
*/
|
*/
|
||||||
export function useExpenseFormTableColumns() {
|
export function useExpenseFormTableColumns({ landedCost }) {
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -127,15 +131,19 @@ export function useExpenseFormTableColumns() {
|
|||||||
className: 'description',
|
className: 'description',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
|
...(landedCost
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
Header: LandedCostHeaderCell,
|
Header: LandedCostHeaderCell,
|
||||||
accessor: 'landed_cost',
|
accessor: 'landed_cost',
|
||||||
Cell: CheckBoxFieldCell,
|
Cell: CheckBoxFieldCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
disableResizing: true,
|
disableResizing: true,
|
||||||
width: 70,
|
width: 100,
|
||||||
className: 'landed_cost',
|
className: 'landed-cost',
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
Header: '',
|
Header: '',
|
||||||
accessor: 'action',
|
accessor: 'action',
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { transformToForm, repeatValue } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
transformToForm,
|
||||||
|
repeatValue,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
const ERROR = {
|
const ERROR = {
|
||||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||||
@@ -27,7 +31,7 @@ export const defaultExpenseEntry = {
|
|||||||
amount: '',
|
amount: '',
|
||||||
expense_account_id: '',
|
expense_account_id: '',
|
||||||
description: '',
|
description: '',
|
||||||
landed_cost: 0,
|
landed_cost: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExpense = {
|
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 React from 'react';
|
||||||
import { FastField, Field, ErrorMessage } from 'formik';
|
import { useFormikContext, FastField, Field, ErrorMessage } from 'formik';
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Classes,
|
Classes,
|
||||||
@@ -23,12 +23,21 @@ import withSettings from 'containers/Settings/withSettings';
|
|||||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
||||||
|
|
||||||
import { compose, inputIntent } from 'utils';
|
import { compose, inputIntent } from 'utils';
|
||||||
|
import {
|
||||||
|
sellDescriptionFieldShouldUpdate,
|
||||||
|
sellAccountFieldShouldUpdate,
|
||||||
|
sellPriceFieldShouldUpdate,
|
||||||
|
costPriceFieldShouldUpdate,
|
||||||
|
costAccountFieldShouldUpdate,
|
||||||
|
purchaseDescFieldShouldUpdate,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item form body.
|
* Item form body.
|
||||||
*/
|
*/
|
||||||
function ItemFormBody({ baseCurrency }) {
|
function ItemFormBody({ baseCurrency }) {
|
||||||
const { accounts } = useItemFormContext();
|
const { accounts } = useItemFormContext();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="page-form__section page-form__section--selling-cost">
|
<div class="page-form__section page-form__section--selling-cost">
|
||||||
@@ -53,7 +62,11 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------- Selling price ------------- */}
|
{/*------------- Selling price ------------- */}
|
||||||
<FastField name={'sell_price'}>
|
<FastField
|
||||||
|
name={'sell_price'}
|
||||||
|
sellable={values.sellable}
|
||||||
|
shouldUpdate={sellPriceFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'selling_price'} />}
|
label={<T id={'selling_price'} />}
|
||||||
@@ -78,7 +91,12 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------- Selling account ------------- */}
|
{/*------------- 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 } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'account'} />}
|
label={<T id={'account'} />}
|
||||||
@@ -107,7 +125,11 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'sell_description'}>
|
<FastField
|
||||||
|
name={'sell_description'}
|
||||||
|
sellable={values.sellable}
|
||||||
|
shouldUpdate={sellDescriptionFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form: { values }, field, meta: { error, touched } }) => (
|
{({ form: { values }, field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'description'} />}
|
label={<T id={'description'} />}
|
||||||
@@ -146,7 +168,11 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------- Cost price ------------- */}
|
{/*------------- Cost price ------------- */}
|
||||||
<FastField name={'cost_price'}>
|
<FastField
|
||||||
|
name={'cost_price'}
|
||||||
|
purchasable={values.purchasable}
|
||||||
|
shouldUpdate={costPriceFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ field, form, field: { value }, meta: { error, touched } }) => (
|
{({ field, form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'cost_price'} />}
|
label={<T id={'cost_price'} />}
|
||||||
@@ -171,7 +197,12 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*------------- Cost account ------------- */}
|
{/*------------- 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 } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'account'} />}
|
label={<T id={'account'} />}
|
||||||
@@ -200,7 +231,11 @@ function ItemFormBody({ baseCurrency }) {
|
|||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'purchase_description'}>
|
<FastField
|
||||||
|
name={'purchase_description'}
|
||||||
|
purchasable={values.purchasable}
|
||||||
|
shouldUpdate={purchaseDescFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form: { values }, field, meta: { error, touched } }) => (
|
{({ form: { values }, field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'description'} />}
|
label={<T id={'description'} />}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import classNames from 'classnames';
|
|||||||
|
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
|
import { accountsFieldShouldUpdate } from './utils';
|
||||||
import { compose, inputIntent } from 'utils';
|
import { compose, inputIntent } from 'utils';
|
||||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||||
import { useItemFormContext } from './ItemFormProvider';
|
import { useItemFormContext } from './ItemFormProvider';
|
||||||
@@ -27,7 +28,11 @@ function ItemFormInventorySection({ baseCurrency }) {
|
|||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
{/*------------- Inventory account ------------- */}
|
{/*------------- Inventory account ------------- */}
|
||||||
<FastField name={'inventory_account_id'}>
|
<FastField
|
||||||
|
name={'inventory_account_id'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'inventory_account'} />}
|
label={<T id={'inventory_account'} />}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { CLASSES } from 'common/classes';
|
|||||||
|
|
||||||
import { useItemFormContext } from './ItemFormProvider';
|
import { useItemFormContext } from './ItemFormProvider';
|
||||||
import { handleStringChange, inputIntent } from 'utils';
|
import { handleStringChange, inputIntent } from 'utils';
|
||||||
|
import { categoriesFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item form primary section.
|
* Item form primary section.
|
||||||
@@ -130,7 +131,11 @@ export default function ItemFormPrimarySection() {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/*----------- Item category ----------*/}
|
{/*----------- Item category ----------*/}
|
||||||
<FastField name={'category_id'}>
|
<FastField
|
||||||
|
name={'category_id'}
|
||||||
|
categories={itemsCategories}
|
||||||
|
shouldUpdate={categoriesFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'category'} />}
|
label={<T id={'category'} />}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
|
import { defaultFastFieldShouldUpdate } from 'utils';
|
||||||
|
|
||||||
export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
|
export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
|
||||||
const table = {
|
const table = {
|
||||||
@@ -28,7 +29,9 @@ export const handleDeleteErrors = (errors) => {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
AppToaster.show({
|
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,
|
intent: Intent.DANGER,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -38,8 +41,83 @@ export const handleDeleteErrors = (errors) => {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
AppToaster.show({
|
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,
|
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,
|
currency_code: baseCurrency,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
[bill],
|
[bill, baseCurrency],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transform response error to fields.
|
// Transform response error to fields.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||||||
|
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { ContactSelecetList, FieldRequiredHint, Icon } from 'components';
|
import { ContactSelecetList, FieldRequiredHint, Icon } from 'components';
|
||||||
|
import { vendorsFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
import { useBillFormContext } from './BillFormProvider';
|
import { useBillFormContext } from './BillFormProvider';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
@@ -28,7 +29,11 @@ function BillFormHeader() {
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ------- Vendor name ------ */}
|
{/* ------- Vendor name ------ */}
|
||||||
<FastField name={'vendor_id'}>
|
<FastField
|
||||||
|
name={'vendor_id'}
|
||||||
|
vendors={vendors}
|
||||||
|
shouldUpdate={vendorsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'vendor_name'} />}
|
label={<T id={'vendor_name'} />}
|
||||||
|
|||||||
@@ -4,13 +4,23 @@ import { FastField } from 'formik';
|
|||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { useBillFormContext } from './BillFormProvider';
|
import { useBillFormContext } from './BillFormProvider';
|
||||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||||
|
import {
|
||||||
|
entriesFieldShouldUpdate
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bill form body.
|
||||||
|
*/
|
||||||
export default function BillFormBody({ defaultBill }) {
|
export default function BillFormBody({ defaultBill }) {
|
||||||
const { items } = useBillFormContext();
|
const { items } = useBillFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<FastField name={'entries'}>
|
<FastField
|
||||||
|
name={'entries'}
|
||||||
|
items={items}
|
||||||
|
shouldUpdate={entriesFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({
|
{({
|
||||||
form: { values, setFieldValue },
|
form: { values, setFieldValue },
|
||||||
field: { value },
|
field: { value },
|
||||||
@@ -25,6 +35,7 @@ export default function BillFormBody({ defaultBill }) {
|
|||||||
errors={error}
|
errors={error}
|
||||||
linesNumber={4}
|
linesNumber={4}
|
||||||
currencyCode={values.currency_code}
|
currencyCode={values.currency_code}
|
||||||
|
landedCost={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import moment from 'moment';
|
|||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import { transformToForm, repeatValue } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
transformToForm,
|
||||||
|
repeatValue,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 4;
|
export const MIN_LINES_NUMBER = 4;
|
||||||
|
|
||||||
@@ -13,6 +17,7 @@ export const defaultBillEntry = {
|
|||||||
discount: '',
|
discount: '',
|
||||||
quantity: '',
|
quantity: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
landed_cost: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultBill = {
|
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>
|
</If>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
// icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
icon={<Icon icon="receipt-24" iconSize={16} />}
|
||||||
text={intl.get('allocate_landed_coast')}
|
text={intl.get('allocate_landed_coast')}
|
||||||
onClick={safeCallback(onAllocateLandedCost, original)}
|
onClick={safeCallback(onAllocateLandedCost, original)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
fullAmountPaymentEntries,
|
fullAmountPaymentEntries,
|
||||||
amountPaymentEntries,
|
amountPaymentEntries,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
import { accountsFieldShouldUpdate, vendorsFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment made form header fields.
|
* Payment made form header fields.
|
||||||
@@ -48,17 +49,14 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
} = useFormikContext();
|
} = useFormikContext();
|
||||||
|
|
||||||
// Payment made form context.
|
// Payment made form context.
|
||||||
const {
|
const { vendors, accounts, isNewMode, setPaymentVendorId } =
|
||||||
vendors,
|
usePaymentMadeFormContext();
|
||||||
accounts,
|
|
||||||
isNewMode,
|
|
||||||
setPaymentVendorId,
|
|
||||||
} = usePaymentMadeFormContext();
|
|
||||||
|
|
||||||
// Sumation of payable full-amount.
|
// Sumation of payable full-amount.
|
||||||
const payableFullAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [
|
const payableFullAmount = useMemo(
|
||||||
entries,
|
() => safeSumBy(entries, 'due_amount'),
|
||||||
]);
|
[entries],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle receive full-amount click.
|
// Handle receive full-amount click.
|
||||||
const handleReceiveFullAmountClick = () => {
|
const handleReceiveFullAmountClick = () => {
|
||||||
@@ -78,7 +76,11 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ------------ Vendor name ------------ */}
|
{/* ------------ Vendor name ------------ */}
|
||||||
<FastField name={'vendor_id'}>
|
<FastField
|
||||||
|
name={'vendor_id'}
|
||||||
|
vendors={vendors}
|
||||||
|
shouldUpdate={vendorsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'vendor_name'} />}
|
label={<T id={'vendor_name'} />}
|
||||||
@@ -184,7 +186,11 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ------------ Payment account ------------ */}
|
{/* ------------ Payment account ------------ */}
|
||||||
<FastField name={'payment_account_id'}>
|
<FastField
|
||||||
|
name={'payment_account_id'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'payment_account'} />}
|
label={<T id={'payment_account'} />}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { safeSumBy, transformToForm } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
safeSumBy,
|
||||||
|
transformToForm,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
||||||
@@ -9,10 +13,10 @@ export const ERRORS = {
|
|||||||
export const defaultPaymentMadeEntry = {
|
export const defaultPaymentMadeEntry = {
|
||||||
bill_id: '',
|
bill_id: '',
|
||||||
payment_amount: '',
|
payment_amount: '',
|
||||||
currency_code:'',
|
currency_code: '',
|
||||||
id: null,
|
id: null,
|
||||||
due_amount: null,
|
due_amount: null,
|
||||||
amount:''
|
amount: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default initial values of payment made.
|
// Default initial values of payment made.
|
||||||
@@ -48,7 +52,26 @@ export const transformToNewPageEntries = (entries) => {
|
|||||||
return entries.map((entry) => ({
|
return entries.map((entry) => ({
|
||||||
...transformToForm(entry, defaultPaymentMadeEntry),
|
...transformToForm(entry, defaultPaymentMadeEntry),
|
||||||
payment_amount: '',
|
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 (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
<EstimateFormHeaderFields />
|
<EstimateFormHeaderFields />
|
||||||
|
|
||||||
<PageFormBigNumber
|
<PageFormBigNumber
|
||||||
label={intl.get('amount')}
|
label={intl.get('amount')}
|
||||||
amount={totalDueAmount}
|
amount={totalDueAmount}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
inputIntent,
|
inputIntent,
|
||||||
handleDateChange,
|
handleDateChange,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
import { customersFieldShouldUpdate } from './utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
@@ -67,7 +68,11 @@ function EstimateFormHeader({
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<FastField name={'customer_id'}>
|
<FastField
|
||||||
|
name={'customer_id'}
|
||||||
|
customers={customers}
|
||||||
|
shouldUpdate={customersFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
@@ -170,7 +175,9 @@ function EstimateFormHeader({
|
|||||||
}}
|
}}
|
||||||
tooltip={true}
|
tooltip={true}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
content: <T id={'setting_your_auto_generated_estimate_number'}/>,
|
content: (
|
||||||
|
<T id={'setting_your_auto_generated_estimate_number'} />
|
||||||
|
),
|
||||||
position: Position.BOTTOM_LEFT,
|
position: Position.BOTTOM_LEFT,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import classNames from 'classnames';
|
|||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate form items entries editor.
|
* Estimate form items entries editor.
|
||||||
@@ -13,7 +14,11 @@ export default function EstimateFormItemsEntriesField() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<FastField name={'entries'}>
|
<FastField
|
||||||
|
name={'entries'}
|
||||||
|
items={items}
|
||||||
|
shouldUpdate={entriesFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({
|
{({
|
||||||
form: { values, setFieldValue },
|
form: { values, setFieldValue },
|
||||||
field: { value },
|
field: { value },
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { transactionNumber, repeatValue, transformToForm } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
transactionNumber,
|
||||||
|
repeatValue,
|
||||||
|
transformToForm,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 4;
|
export const MIN_LINES_NUMBER = 4;
|
||||||
|
|
||||||
@@ -49,4 +54,24 @@ export const useObserveEstimateNoSettings = (prefix, nextNumber) => {
|
|||||||
const estimateNo = transactionNumber(prefix, nextNumber);
|
const estimateNo = transactionNumber(prefix, nextNumber);
|
||||||
setFieldValue('estimate_number', estimateNo);
|
setFieldValue('estimate_number', estimateNo);
|
||||||
}, [setFieldValue, prefix, nextNumber]);
|
}, [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 { FormattedMessage as T } from 'components';
|
||||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useObserveInvoiceNoSettings } from './utils';
|
import {
|
||||||
|
useObserveInvoiceNoSettings,
|
||||||
|
customerNameFieldShouldUpdate,
|
||||||
|
} from './utils';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
ContactSelecetList,
|
ContactSelecetList,
|
||||||
@@ -58,15 +61,16 @@ function InvoiceFormHeaderFields({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Syncs invoice number settings with form.
|
// Syncs invoice number settings with form.
|
||||||
useObserveInvoiceNoSettings(
|
useObserveInvoiceNoSettings(invoiceNumberPrefix, invoiceNextNumber);
|
||||||
invoiceNumberPrefix,
|
|
||||||
invoiceNextNumber,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<FastField name={'customer_id'}>
|
<FastField
|
||||||
|
name={'customer_id'}
|
||||||
|
customers={customers}
|
||||||
|
shouldUpdate={customerNameFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
@@ -168,7 +172,9 @@ function InvoiceFormHeaderFields({
|
|||||||
}}
|
}}
|
||||||
tooltip={true}
|
tooltip={true}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
content: <T id={'setting_your_auto_generated_invoice_number'}/>,
|
content: (
|
||||||
|
<T id={'setting_your_auto_generated_invoice_number'} />
|
||||||
|
),
|
||||||
position: Position.BOTTOM_LEFT,
|
position: Position.BOTTOM_LEFT,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import classNames from 'classnames';
|
|||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice items entries editor field.
|
* Invoice items entries editor field.
|
||||||
@@ -13,7 +14,11 @@ export default function InvoiceItemsEntriesEditorField() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<FastField name={'entries'}>
|
<FastField
|
||||||
|
name={'entries'}
|
||||||
|
items={items}
|
||||||
|
shouldUpdate={entriesFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({
|
{({
|
||||||
form: { values, setFieldValue },
|
form: { values, setFieldValue },
|
||||||
field: { value },
|
field: { value },
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { updateItemsEntriesTotal } from 'containers/Entries/utils';
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
import { orderingLinesIndexes } from 'utils';
|
import { defaultFastFieldShouldUpdate } from 'utils';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { ERROR } from 'common/errors';
|
import { ERROR } from 'common/errors';
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
@@ -101,3 +101,17 @@ export const useObserveInvoiceNoSettings = (prefix, nextNumber) => {
|
|||||||
setFieldValue('invoice_no', invoiceNo);
|
setFieldValue('invoice_no', invoiceNo);
|
||||||
}, [setFieldValue, prefix, nextNumber]);
|
}, [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';
|
} from 'components';
|
||||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||||
|
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
@@ -41,6 +42,8 @@ import {
|
|||||||
useObservePaymentNoSettings,
|
useObservePaymentNoSettings,
|
||||||
amountPaymentEntries,
|
amountPaymentEntries,
|
||||||
fullAmountPaymentEntries,
|
fullAmountPaymentEntries,
|
||||||
|
customersFieldShouldUpdate,
|
||||||
|
accountsFieldShouldUpdate,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { toSafeInteger } from 'lodash';
|
import { toSafeInteger } from 'lodash';
|
||||||
|
|
||||||
@@ -115,7 +118,11 @@ function PaymentReceiveHeaderFields({
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ------------- Customer name ------------- */}
|
{/* ------------- Customer name ------------- */}
|
||||||
<FastField name={'customer_id'}>
|
<FastField
|
||||||
|
name={'customer_id'}
|
||||||
|
customers={customers}
|
||||||
|
shouldUpdate={customersFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
@@ -247,7 +254,11 @@ function PaymentReceiveHeaderFields({
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ------------ Deposit account ------------ */}
|
{/* ------------ Deposit account ------------ */}
|
||||||
<FastField name={'deposit_account_id'}>
|
<FastField
|
||||||
|
name={'deposit_account_id'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'deposit_to'} />}
|
label={<T id={'deposit_to'} />}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { transactionNumber, transformToForm, safeSumBy } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
transactionNumber,
|
||||||
|
transformToForm,
|
||||||
|
safeSumBy,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
// Default payment receive entry.
|
// Default payment receive entry.
|
||||||
export const defaultPaymentReceiveEntry = {
|
export const defaultPaymentReceiveEntry = {
|
||||||
@@ -99,3 +104,23 @@ export const useObservePaymentNoSettings = (prefix, nextNumber) => {
|
|||||||
setFieldValue('payment_receive_no', invoiceNo);
|
setFieldValue('payment_receive_no', invoiceNo);
|
||||||
}, [setFieldValue, prefix, nextNumber]);
|
}, [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,
|
inputIntent,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
import { useObserveReceiptNoSettings } from './utils';
|
import {
|
||||||
|
accountsFieldShouldUpdate,
|
||||||
|
customersFieldShouldUpdate,
|
||||||
|
useObserveReceiptNoSettings,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt form header fields.
|
* Receipt form header fields.
|
||||||
@@ -70,7 +74,11 @@ function ReceiptFormHeader({
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<FastField name={'customer_id'}>
|
<FastField
|
||||||
|
name={'customer_id'}
|
||||||
|
customers={customers}
|
||||||
|
shouldUpdate={customersFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
@@ -94,7 +102,11 @@ function ReceiptFormHeader({
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ----------- Deposit account ----------- */}
|
{/* ----------- Deposit account ----------- */}
|
||||||
<FastField name={'deposit_account_id'}>
|
<FastField
|
||||||
|
name={'deposit_account_id'}
|
||||||
|
accounts={accounts}
|
||||||
|
shouldUpdate={accountsFieldShouldUpdate}
|
||||||
|
>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'deposit_account'} />}
|
label={<T id={'deposit_account'} />}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { FastField } from 'formik';
|
|||||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
export default function ReceiptItemsEntriesEditor({ defaultReceipt }) {
|
export default function ReceiptItemsEntriesEditor({ defaultReceipt }) {
|
||||||
const { items } = useReceiptFormContext();
|
const { items } = useReceiptFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<FastField name={'entries'}>
|
<FastField name={'entries'} items={items} shouldUpdate={entriesFieldShouldUpdate}>
|
||||||
{({
|
{({
|
||||||
form: { values, setFieldValue },
|
form: { values, setFieldValue },
|
||||||
field: { value },
|
field: { value },
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { transactionNumber, repeatValue, transformToForm } from 'utils';
|
import {
|
||||||
|
defaultFastFieldShouldUpdate,
|
||||||
|
transactionNumber,
|
||||||
|
repeatValue,
|
||||||
|
transformToForm,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 4;
|
export const MIN_LINES_NUMBER = 4;
|
||||||
|
|
||||||
@@ -42,7 +47,6 @@ export const transformToEditForm = (receipt) => ({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
|
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
@@ -50,4 +54,34 @@ export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
|
|||||||
const receiptNo = transactionNumber(prefix, nextNumber);
|
const receiptNo = transactionNumber(prefix, nextNumber);
|
||||||
setFieldValue('receipt_number', receiptNo);
|
setFieldValue('receipt_number', receiptNo);
|
||||||
}, [setFieldValue, prefix, nextNumber]);
|
}, [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",
|
"No items": "No items",
|
||||||
"cannot_delete_bill_that_has_associated_landed_cost_transactions": "Cannot delete bill that has associated landed cost transactions.",
|
"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",
|
"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,
|
.th,
|
||||||
.td {
|
.td {
|
||||||
border-left: 1px dashed #e2e2e2;
|
border-left: 1px solid #e2e2e2;
|
||||||
|
|
||||||
&.index {
|
&.index {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -55,6 +55,19 @@
|
|||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.landed-cost{
|
||||||
|
|
||||||
|
.bp3-control{
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 34px;
|
||||||
|
}
|
||||||
|
.bp3-control-indicator{
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
border-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.tr {
|
.tr {
|
||||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
.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 {
|
.account-drawer {
|
||||||
background-color: #fbfbfb;
|
background-color: #fbfbfb;
|
||||||
@@ -95,28 +85,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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
.bp3-tabs {
|
.bp3-tabs {
|
||||||
.bp3-tab-list {
|
.bp3-tab-list {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background-color: #FFF;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: '';
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: #f0f0f0;
|
background: #e1e2e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
> *:not(:last-child) {
|
> *:not(:last-child) {
|
||||||
@@ -29,14 +30,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bp3-tab-panel{
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
.card{
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bigcapital-datatable {
|
.datatable--landed-cost-transactions {
|
||||||
.table {
|
.table {
|
||||||
max-height: 500px;
|
|
||||||
border: 1px solid #d1dee2;
|
|
||||||
min-width: auto;
|
|
||||||
margin: 12px;
|
|
||||||
|
|
||||||
.tbody,
|
.tbody,
|
||||||
.tbody-inner {
|
.tbody-inner {
|
||||||
@@ -48,34 +53,13 @@
|
|||||||
}
|
}
|
||||||
.tbody {
|
.tbody {
|
||||||
.tr .td {
|
.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;
|
top: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.bp3-drawer-header .bp3-heading {
|
.bp3-drawer-header .bp3-heading {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
.journal-drawer,
|
.journal-drawer,
|
||||||
.expense-drawer {
|
.expense-drawer {
|
||||||
background: #f5f5f5;
|
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -18,8 +17,8 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
margin: 15px 0 20px;
|
margin: 15px 0 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
// color: #333333;
|
|
||||||
color: #666666;
|
color: #666666;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
span {
|
span {
|
||||||
@@ -44,17 +43,17 @@
|
|||||||
&--table {
|
&--table {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
color: #666666;
|
color: #666666;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
.thead .tr .th .resizer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.thead .th {
|
.thead .th {
|
||||||
|
background: transparent;
|
||||||
color: #222222;
|
color: #222222;
|
||||||
border-bottom: 1px solid #000000;
|
border-bottom: 1px solid #000000;
|
||||||
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
.thead .th,
|
|
||||||
.tbody .tr .td {
|
.tbody .tr .td {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0.8rem 0.5rem;
|
padding: 0.8rem 0.5rem;
|
||||||
@@ -63,7 +62,6 @@
|
|||||||
.desc {
|
.desc {
|
||||||
margin: 20px 0 60px;
|
margin: 20px 0 60px;
|
||||||
|
|
||||||
// margin: 20px 0;
|
|
||||||
> b {
|
> b {
|
||||||
color: #2f2f2f;
|
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;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.page-form--bill{
|
.page-form--bill{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
|
.dashboard__insider--expenses {
|
||||||
|
|
||||||
.dashboard__insider--expenses{
|
.bigcapital-datatable {
|
||||||
|
|
||||||
.bigcapital-datatable{
|
.tbody {
|
||||||
|
.tr .td.total_amount {
|
||||||
.tbody{
|
span {
|
||||||
.tr .td.total_amount{
|
|
||||||
span{
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,36 +12,64 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-form--expense{
|
.page-form--expense {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
#{$self}__header{
|
#{$self}__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&-fields{
|
&-fields {
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-label{
|
.bp3-label {
|
||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
}
|
}
|
||||||
.bp3-form-content{
|
|
||||||
|
.bp3-form-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-form-group{
|
.bp3-form-group {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
|
|
||||||
&.bp3-inline{
|
&.bp3-inline {
|
||||||
max-width: 440px;
|
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;
|
max-width: 500px;
|
||||||
|
|
||||||
textarea{
|
textarea {
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// Noto Sans
|
||||||
|
// -------------------------------------
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Noto Sans;
|
font-family: Noto Sans;
|
||||||
src: local('Noto Sans'), url('../fonts/NotoSans-SemiBold.woff') format('woff');
|
src: local('Noto Sans'), url('../fonts/NotoSans-SemiBold.woff') format('woff');
|
||||||
@@ -30,46 +34,8 @@
|
|||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// arabic regular
|
// Segoe UI Arabic
|
||||||
@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 - Regular
|
// Segoe UI Arabic - Regular
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Segoe UI';
|
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>");
|
$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;
|
$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,
|
Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue,
|
||||||
Icons16, sans-serif;
|
Icons16, sans-serif;
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ export const objectKeysTransform = (obj, transform) => {
|
|||||||
|
|
||||||
export const compose = (...funcs) =>
|
export const compose = (...funcs) =>
|
||||||
funcs.reduce(
|
funcs.reduce(
|
||||||
(a, b) => (...args) => a(b(...args)),
|
(a, b) =>
|
||||||
|
(...args) =>
|
||||||
|
a(b(...args)),
|
||||||
(arg) => arg,
|
(arg) => arg,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -639,7 +641,32 @@ const getCurrenciesOptions = () => {
|
|||||||
currency_code: currencyCode,
|
currency_code: currencyCode,
|
||||||
formatted_name: `${currencyCode} - ${currency.name}`,
|
formatted_name: `${currencyCode} - ${currency.name}`,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const currenciesOptions = getCurrenciesOptions();
|
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 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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export default class CashFlowController extends BaseFinancialReportController {
|
|||||||
/**
|
/**
|
||||||
* Transformes the report statement to table rows.
|
* Transformes the report statement to table rows.
|
||||||
* @param {ITransactionsByVendorsStatement} statement -
|
* @param {ITransactionsByVendorsStatement} statement -
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private transformToTableRows(cashFlowDOO: ICashFlowStatementDOO, tenantId: number) {
|
private transformToTableRows(cashFlowDOO: ICashFlowStatementDOO, tenantId: number) {
|
||||||
const i18n = this.tenancy.i18n(tenantId);
|
const i18n = this.tenancy.i18n(tenantId);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR
|
|||||||
* Transformes the report statement to table rows.
|
* Transformes the report statement to table rows.
|
||||||
* @param {IVendorBalanceSummaryStatement} statement -
|
* @param {IVendorBalanceSummaryStatement} statement -
|
||||||
*/
|
*/
|
||||||
transformToTableRows({ data }: IVendorBalanceSummaryStatement) {
|
private transformToTableRows({ data }: IVendorBalanceSummaryStatement) {
|
||||||
return {
|
return {
|
||||||
table: {
|
table: {
|
||||||
data: this.vendorBalanceSummaryTableRows.tableRowsTransformer(data),
|
data: this.vendorBalanceSummaryTableRows.tableRowsTransformer(data),
|
||||||
@@ -57,7 +57,10 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR
|
|||||||
* Transformes the report statement to raw json.
|
* Transformes the report statement to raw json.
|
||||||
* @param {IVendorBalanceSummaryStatement} statement -
|
* @param {IVendorBalanceSummaryStatement} statement -
|
||||||
*/
|
*/
|
||||||
transformToJsonResponse({ data, columns }: IVendorBalanceSummaryStatement) {
|
private transformToJsonResponse({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
}: IVendorBalanceSummaryStatement) {
|
||||||
return {
|
return {
|
||||||
data: this.transfromToResponse(data),
|
data: this.transfromToResponse(data),
|
||||||
columns: this.transfromToResponse(columns),
|
columns: this.transfromToResponse(columns),
|
||||||
@@ -76,7 +79,8 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR
|
|||||||
const filter = this.matchedQueryData(req);
|
const filter = this.matchedQueryData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const vendorBalanceSummary = await this.vendorBalanceSummaryService.vendorBalanceSummary(
|
const vendorBalanceSummary =
|
||||||
|
await this.vendorBalanceSummaryService.vendorBalanceSummary(
|
||||||
tenantId,
|
tenantId,
|
||||||
filter
|
filter
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -192,7 +192,10 @@ export default class BillAllocateLandedCost extends BaseController {
|
|||||||
billId
|
billId
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({ billId, transactions });
|
return res.status(200).send({
|
||||||
|
billId,
|
||||||
|
transactions: this.transfromToResponse(transactions)
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { ref } from 'objection';
|
import { ref, transaction } from 'objection';
|
||||||
import {
|
import {
|
||||||
ILandedCostTransactionsQueryDTO,
|
ILandedCostTransactionsQueryDTO,
|
||||||
ILandedCostTransaction,
|
ILandedCostTransaction,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import TransactionLandedCost from './TransctionLandedCost';
|
import TransactionLandedCost from './TransctionLandedCost';
|
||||||
import BillsService from '../Bills';
|
import BillsService from '../Bills';
|
||||||
import HasTenancyService from 'services/Tenancy/TenancyService';
|
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
import { formatNumber } from 'utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class LandedCostListing {
|
export default class LandedCostListing {
|
||||||
@@ -71,8 +72,15 @@ export default class LandedCostListing {
|
|||||||
|
|
||||||
const landedCostTransactions = await BillLandedCost.query()
|
const landedCostTransactions = await BillLandedCost.query()
|
||||||
.where('bill_id', billId)
|
.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
|
||||||
|
),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user