mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
feature : Puschases & Sales / fix : tasks
This commit is contained in:
@@ -9,6 +9,7 @@ export default function AccountsSelectList({
|
|||||||
selectedAccountId,
|
selectedAccountId,
|
||||||
defaultSelectText = 'Select account',
|
defaultSelectText = 'Select account',
|
||||||
onAccountSelected,
|
onAccountSelected,
|
||||||
|
disabled = false,
|
||||||
}) {
|
}) {
|
||||||
// Find initial account object to set it as default account in initial render.
|
// Find initial account object to set it as default account in initial render.
|
||||||
const initialAccount = useMemo(
|
const initialAccount = useMemo(
|
||||||
@@ -77,6 +78,7 @@ export default function AccountsSelectList({
|
|||||||
onItemSelect={onAccountSelect}
|
onItemSelect={onAccountSelect}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
text={selectedAccount ? selectedAccount.name : defaultSelectText}
|
text={selectedAccount ? selectedAccount.name : defaultSelectText}
|
||||||
/>
|
/>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
export default function DivFieldCell({ cell: { value: initialValue } }) {
|
export const DivFieldCell = ({ cell: { value: initialValue } }) => {
|
||||||
const [value, setValue] = useState(initialValue);
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -8,4 +8,13 @@ export default function DivFieldCell({ cell: { value: initialValue } }) {
|
|||||||
}, [initialValue]);
|
}, [initialValue]);
|
||||||
|
|
||||||
return <div>${value}</div>;
|
return <div>${value}</div>;
|
||||||
}
|
};
|
||||||
|
export const EmptyDiv = ({ cell: { value: initialValue } }) => {
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return <div>{value}</div>;
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import InputGroupCell from './InputGroupCell';
|
|||||||
import ContactsListFieldCell from './ContactsListFieldCell';
|
import ContactsListFieldCell from './ContactsListFieldCell';
|
||||||
import EstimatesListFieldCell from './EstimatesListFieldCell';
|
import EstimatesListFieldCell from './EstimatesListFieldCell';
|
||||||
import PercentFieldCell from './PercentFieldCell';
|
import PercentFieldCell from './PercentFieldCell';
|
||||||
import DivFieldCell from './DivFieldCell';
|
import { DivFieldCell,EmptyDiv } from './DivFieldCell';
|
||||||
export {
|
export {
|
||||||
AccountsListFieldCell,
|
AccountsListFieldCell,
|
||||||
MoneyFieldCell,
|
MoneyFieldCell,
|
||||||
@@ -13,4 +13,5 @@ export {
|
|||||||
EstimatesListFieldCell,
|
EstimatesListFieldCell,
|
||||||
PercentFieldCell,
|
PercentFieldCell,
|
||||||
DivFieldCell,
|
DivFieldCell,
|
||||||
|
EmptyDiv,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useState, useMemo, useEffect, useCallback} from 'react';
|
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
import {InputGroup} from '@blueprintjs/core';
|
import { InputGroup } from '@blueprintjs/core';
|
||||||
|
|
||||||
const joinIntegerAndDecimal = (integer, decimal, separator) => {
|
const joinIntegerAndDecimal = (integer, decimal, separator) => {
|
||||||
let output = `${integer}`;
|
let output = `${integer}`;
|
||||||
@@ -16,13 +16,13 @@ const hasSeparator = (input, separator) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addThousandSeparator = (integer, separator) => {
|
const addThousandSeparator = (integer, separator) => {
|
||||||
return integer.replace(/(\d)(?=(?:\d{3})+\b)/gm, `$1${separator}`)
|
return integer.replace(/(\d)(?=(?:\d{3})+\b)/gm, `$1${separator}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toString = (number) => `${number}`;
|
const toString = (number) => `${number}`;
|
||||||
|
|
||||||
const onlyNumbers = (input) => {
|
const onlyNumbers = (input) => {
|
||||||
return toString(input).replace(/\D+/g, '') || '0'
|
return toString(input).replace(/\D+/g, '') || '0';
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatter = (value, options) => {
|
const formatter = (value, options) => {
|
||||||
@@ -31,11 +31,19 @@ const formatter = (value, options) => {
|
|||||||
const parts = toString(input).split(options.decimal);
|
const parts = toString(input).split(options.decimal);
|
||||||
const integer = parseInt(onlyNumbers(parts[0]), 10);
|
const integer = parseInt(onlyNumbers(parts[0]), 10);
|
||||||
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
|
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
|
||||||
const integerThousand = addThousandSeparator(toString(integer), options.thousands);
|
const integerThousand = addThousandSeparator(
|
||||||
|
toString(integer),
|
||||||
|
options.thousands,
|
||||||
|
);
|
||||||
const separator = hasSeparator(input, options.decimal)
|
const separator = hasSeparator(input, options.decimal)
|
||||||
? options.decimal : false;
|
? options.decimal
|
||||||
|
: false;
|
||||||
|
|
||||||
return `${navigate}${options.prefix}${joinIntegerAndDecimal(integerThousand, decimal, separator)}${options.suffix}`;
|
return `${navigate}${options.prefix}${joinIntegerAndDecimal(
|
||||||
|
integerThousand,
|
||||||
|
decimal,
|
||||||
|
separator,
|
||||||
|
)}${options.suffix}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const unformatter = (input, options) => {
|
const unformatter = (input, options) => {
|
||||||
@@ -44,12 +52,12 @@ const unformatter = (input, options) => {
|
|||||||
const integer = parseInt(onlyNumbers(parts[0]), 10);
|
const integer = parseInt(onlyNumbers(parts[0]), 10);
|
||||||
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
|
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
|
||||||
const separator = hasSeparator(input, options.decimal)
|
const separator = hasSeparator(input, options.decimal)
|
||||||
? options.decimal : false;
|
? options.decimal
|
||||||
|
: false;
|
||||||
|
|
||||||
return `${navigate}${joinIntegerAndDecimal(integer, decimal, separator)}`;
|
return `${navigate}${joinIntegerAndDecimal(integer, decimal, separator)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default function MoneyFieldGroup({
|
export default function MoneyFieldGroup({
|
||||||
value,
|
value,
|
||||||
prefix = '',
|
prefix = '',
|
||||||
@@ -59,32 +67,43 @@ export default function MoneyFieldGroup({
|
|||||||
precision = 2,
|
precision = 2,
|
||||||
inputGroupProps,
|
inputGroupProps,
|
||||||
onChange,
|
onChange,
|
||||||
|
disabled = false,
|
||||||
}) {
|
}) {
|
||||||
const [state, setState] = useState(value);
|
const [state, setState] = useState(value);
|
||||||
|
|
||||||
const options = useMemo(() => ({
|
const options = useMemo(
|
||||||
prefix, suffix, thousands, decimal, precision,
|
() => ({
|
||||||
}), [
|
prefix,
|
||||||
prefix, suffix, thousands, decimal, precision,
|
suffix,
|
||||||
]);
|
thousands,
|
||||||
|
decimal,
|
||||||
|
precision,
|
||||||
|
}),
|
||||||
|
[prefix, suffix, thousands, decimal, precision],
|
||||||
|
);
|
||||||
|
|
||||||
const handleChange = useCallback((event) => {
|
const handleChange = useCallback(
|
||||||
const formatted = formatter(event.target.value, options);
|
(event) => {
|
||||||
const value = unformatter(event.target.value, options);
|
const formatted = formatter(event.target.value, options);
|
||||||
|
const value = unformatter(event.target.value, options);
|
||||||
|
|
||||||
setState(formatted);
|
setState(formatted);
|
||||||
onChange && onChange(event, value);
|
onChange && onChange(event, value);
|
||||||
}, [onChange, options]);
|
},
|
||||||
|
[onChange, options],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const formatted = formatter(value, options);
|
const formatted = formatter(value, options);
|
||||||
setState(formatted)
|
setState(formatted);
|
||||||
}, [value, options, setState]);
|
}, [value, options, setState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputGroup
|
<InputGroup
|
||||||
value={state}
|
value={state}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
{...inputGroupProps} />
|
{...inputGroupProps}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,37 +40,25 @@ export default [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'estimates'} />,
|
text: <T id={'estimates'} />,
|
||||||
href: '/estimates/new',
|
href: '/estimates',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// text: <T id={'estimate_list'} />,
|
|
||||||
// href: '/estimates',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
text: <T id={'invocies'} />,
|
text: <T id={'invocies'} />,
|
||||||
href: '/invoices/new',
|
href: '/invoices',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// text: <T id={'invoices_list'} />,
|
|
||||||
// href: '/invoices',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
text: <T id={'payment_receives'} />,
|
text: <T id={'payment_receives'} />,
|
||||||
href: '/payment-receive/new',
|
href: '/payment-receives',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
divider: true,
|
divider: true,
|
||||||
text: <T id={'invoices_list'} />,
|
|
||||||
href: '/invoices',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'receipts'} />,
|
text: <T id={'receipts'} />,
|
||||||
href: '/receipts/new',
|
href: '/receipts',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// text: <T id={'receipt_list'} />,
|
|
||||||
// href: '/receipts',
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,14 +66,12 @@ export default [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'bills'} />,
|
text: <T id={'bills'} />,
|
||||||
href: '/bills/new',
|
href: '/bills',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// text: <T id={'bill_list'} />,
|
|
||||||
// href: '/bills',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
text: <T id={'payment_mades'} />,
|
text: <T id={'payment_made'} />,
|
||||||
|
href: '/payment-mades',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
import { ListSelect } from 'components';
|
import { ListSelect, AccountsSelectList } from 'components';
|
||||||
|
|
||||||
import Dialog from 'components/Dialog';
|
import Dialog from 'components/Dialog';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withDialogRedux from 'components/DialogReduxConnect';
|
import withDialogRedux from 'components/DialogReduxConnect';
|
||||||
|
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
import withItemCategoryDetail from 'containers/Items/withItemCategoryDetail';
|
import withItemCategoryDetail from 'containers/Items/withItemCategoryDetail';
|
||||||
import withItemCategories from 'containers/Items/withItemCategories';
|
import withItemCategories from 'containers/Items/withItemCategories';
|
||||||
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||||
@@ -48,10 +50,16 @@ function ItemCategoryDialog({
|
|||||||
// #withItemCategories
|
// #withItemCategories
|
||||||
categoriesList,
|
categoriesList,
|
||||||
|
|
||||||
|
//# withAccount
|
||||||
|
accountsList,
|
||||||
|
|
||||||
// #withItemCategoriesActions
|
// #withItemCategoriesActions
|
||||||
requestSubmitItemCategory,
|
requestSubmitItemCategory,
|
||||||
requestFetchItemCategories,
|
requestFetchItemCategories,
|
||||||
requestEditItemCategory,
|
requestEditItemCategory,
|
||||||
|
|
||||||
|
// #withAccountsActions
|
||||||
|
requestFetchAccounts,
|
||||||
}) {
|
}) {
|
||||||
const [selectedParentCategory, setParentCategory] = useState(null);
|
const [selectedParentCategory, setParentCategory] = useState(null);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@@ -59,12 +67,23 @@ function ItemCategoryDialog({
|
|||||||
const fetchList = useQuery(['items-categories-list'], () =>
|
const fetchList = useQuery(['items-categories-list'], () =>
|
||||||
requestFetchItemCategories(),
|
requestFetchItemCategories(),
|
||||||
);
|
);
|
||||||
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
name: Yup.string()
|
name: Yup.string()
|
||||||
.required()
|
.required()
|
||||||
.label(formatMessage({ id: 'category_name_' })),
|
.label(formatMessage({ id: 'category_name_' })),
|
||||||
parent_category_id: Yup.string().nullable(),
|
parent_category_id: Yup.string().nullable(),
|
||||||
|
cost_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'cost_account_' })),
|
||||||
|
sell_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'sell_account_' })),
|
||||||
|
inventory_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'inventory_account_' })),
|
||||||
description: Yup.string().trim(),
|
description: Yup.string().trim(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,6 +92,9 @@ function ItemCategoryDialog({
|
|||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
|
cost_account_id: null,
|
||||||
|
sell_account_id: null,
|
||||||
|
inventory_account_id: null,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -95,18 +117,21 @@ function ItemCategoryDialog({
|
|||||||
},
|
},
|
||||||
validationSchema,
|
validationSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
|
const afterSubmit = () => {
|
||||||
|
closeDialog(dialogName);
|
||||||
|
queryCache.invalidateQueries('items-categories-table');
|
||||||
|
queryCache.invalidateQueries('accounts-list');
|
||||||
|
};
|
||||||
if (payload.action === 'edit') {
|
if (payload.action === 'edit') {
|
||||||
requestEditItemCategory(payload.id, values)
|
requestEditItemCategory(payload.id, values)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
closeDialog(dialogName);
|
afterSubmit(response);
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage({
|
||||||
id: 'the_item_category_has_been_successfully_edited',
|
id: 'the_item_category_has_been_successfully_edited',
|
||||||
}),
|
}),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
|
||||||
queryCache.invalidateQueries('items-categories-table');
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -114,15 +139,13 @@ function ItemCategoryDialog({
|
|||||||
} else {
|
} else {
|
||||||
requestSubmitItemCategory(values)
|
requestSubmitItemCategory(values)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
closeDialog(dialogName);
|
afterSubmit(response);
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage({
|
||||||
id: 'the_item_category_has_been_successfully_created',
|
id: 'the_item_category_has_been_successfully_created',
|
||||||
}),
|
}),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
|
||||||
queryCache.invalidateQueries('items-categories-table');
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -130,7 +153,6 @@ function ItemCategoryDialog({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const filterItemCategory = useCallback(
|
const filterItemCategory = useCallback(
|
||||||
(query, category, _index, exactMatch) => {
|
(query, category, _index, exactMatch) => {
|
||||||
const normalizedTitle = category.name.toLowerCase();
|
const normalizedTitle = category.name.toLowerCase();
|
||||||
@@ -166,7 +188,8 @@ function ItemCategoryDialog({
|
|||||||
// Handle the dialog opening.
|
// Handle the dialog opening.
|
||||||
const onDialogOpening = useCallback(() => {
|
const onDialogOpening = useCallback(() => {
|
||||||
fetchList.refetch();
|
fetchList.refetch();
|
||||||
}, [fetchList]);
|
fetchAccounts.refetch();
|
||||||
|
}, [fetchList, fetchAccounts]);
|
||||||
|
|
||||||
const onChangeParentCategory = useCallback(
|
const onChangeParentCategory = useCallback(
|
||||||
(parentCategory) => {
|
(parentCategory) => {
|
||||||
@@ -176,6 +199,15 @@ function ItemCategoryDialog({
|
|||||||
[setFieldValue],
|
[setFieldValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onItemAccountSelect = useCallback(
|
||||||
|
(filedName) => {
|
||||||
|
return (account) => {
|
||||||
|
setFieldValue(filedName, account.id);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
const onDialogClosed = useCallback(() => {
|
const onDialogClosed = useCallback(() => {
|
||||||
resetForm();
|
resetForm();
|
||||||
closeDialog(dialogName);
|
closeDialog(dialogName);
|
||||||
@@ -273,6 +305,79 @@ function ItemCategoryDialog({
|
|||||||
{...getFieldProps('description')}
|
{...getFieldProps('description')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* cost account */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'cost_account'} />}
|
||||||
|
inline={true}
|
||||||
|
intent={
|
||||||
|
errors.cost_account_id && touched.cost_account_id && Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name="cost_account_id" />
|
||||||
|
}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--cost-account',
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AccountsSelectList
|
||||||
|
accounts={accountsList}
|
||||||
|
onAccountSelected={onItemAccountSelect('cost_account_id')}
|
||||||
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
|
selectedAccountId={values.cost_account_id}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* sell Account */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'sell_account'} />}
|
||||||
|
inline={true}
|
||||||
|
intent={
|
||||||
|
errors.sell_account_id && touched.sell_account_id && Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name="sell_account_id" />
|
||||||
|
}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--sell-account',
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AccountsSelectList
|
||||||
|
accounts={accountsList}
|
||||||
|
onAccountSelected={onItemAccountSelect('sell_account_id')}
|
||||||
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
|
selectedAccountId={values.sell_account_id}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
{/* inventory Account */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'inventory_account'} />}
|
||||||
|
inline={true}
|
||||||
|
intent={
|
||||||
|
errors.inventory_account &&
|
||||||
|
touched.inventory_account &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name="inventory_account" />
|
||||||
|
}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--sell-account',
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AccountsSelectList
|
||||||
|
accounts={accountsList}
|
||||||
|
onAccountSelected={onItemAccountSelect('inventory_account')}
|
||||||
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
|
selectedAccountId={values.inventory_account}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
@@ -312,5 +417,9 @@ export default compose(
|
|||||||
withItemCategories(({ categoriesList }) => ({
|
withItemCategories(({ categoriesList }) => ({
|
||||||
categoriesList,
|
categoriesList,
|
||||||
})),
|
})),
|
||||||
|
withAccounts(({ accountsList }) => ({
|
||||||
|
accountsList,
|
||||||
|
})),
|
||||||
withItemCategoriesActions,
|
withItemCategoriesActions,
|
||||||
|
withAccountsActions,
|
||||||
)(ItemCategoryDialog);
|
)(ItemCategoryDialog);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import ItemCategoriesDataTable from 'containers/Items/ItemCategoriesTable';
|
|||||||
import ItemsCategoryActionsBar from 'containers/Items/ItemsCategoryActionsBar';
|
import ItemsCategoryActionsBar from 'containers/Items/ItemsCategoryActionsBar';
|
||||||
|
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
@@ -25,10 +26,15 @@ const ItemCategoryList = ({
|
|||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
|
|
||||||
|
// #withViewsActions
|
||||||
|
requestFetchResourceViews,
|
||||||
|
requestFetchResourceFields,
|
||||||
|
|
||||||
// #withItemCategoriesActions
|
// #withItemCategoriesActions
|
||||||
requestFetchItemCategories,
|
requestFetchItemCategories,
|
||||||
requestDeleteItemCategory,
|
requestDeleteItemCategory,
|
||||||
requestDeleteBulkItemCategories,
|
requestDeleteBulkItemCategories,
|
||||||
|
addItemCategoriesTableQueries,
|
||||||
|
|
||||||
// #withDialog
|
// #withDialog
|
||||||
openDialog,
|
openDialog,
|
||||||
@@ -48,9 +54,13 @@ const ItemCategoryList = ({
|
|||||||
: changePageTitle(formatMessage({ id: 'category_list' }));
|
: changePageTitle(formatMessage({ id: 'category_list' }));
|
||||||
}, [id, changePageTitle, formatMessage]);
|
}, [id, changePageTitle, formatMessage]);
|
||||||
|
|
||||||
const fetchCategories = useQuery(
|
const fetchCategories = useQuery(['items-categories-list'], () =>
|
||||||
['items-categories-list', filter],
|
requestFetchItemCategories(),
|
||||||
(key, query) => requestFetchItemCategories(query),
|
);
|
||||||
|
|
||||||
|
const fetchResourceFields = useQuery(
|
||||||
|
['resource-fields', 'items_categories'],
|
||||||
|
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFilterChanged = useCallback(() => {}, []);
|
const handleFilterChanged = useCallback(() => {}, []);
|
||||||
@@ -63,17 +73,23 @@ const ItemCategoryList = ({
|
|||||||
[setSelectedRows],
|
[setSelectedRows],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle fetch data of accounts datatable.
|
const handleFetchData = useCallback(
|
||||||
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
({ pageIndex, pageSize, sortBy }) => {
|
||||||
setFilter({
|
const page = pageIndex + 1;
|
||||||
...(sortBy.length > 0
|
|
||||||
? {
|
addItemCategoriesTableQueries({
|
||||||
column_sort_by: sortBy[0].id,
|
...(sortBy.length > 0
|
||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
? {
|
||||||
}
|
column_sort_by: sortBy[0].id,
|
||||||
: {}),
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
});
|
}
|
||||||
}, []);
|
: {}),
|
||||||
|
page_size: pageSize,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addItemCategoriesTableQueries],
|
||||||
|
);
|
||||||
|
|
||||||
const handleDeleteCategory = (itemCategory) => {
|
const handleDeleteCategory = (itemCategory) => {
|
||||||
setDeleteCategory(itemCategory);
|
setDeleteCategory(itemCategory);
|
||||||
@@ -139,7 +155,10 @@ const ItemCategoryList = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider name={'item-category-list'}>
|
<DashboardInsider
|
||||||
|
loading={fetchResourceFields.isFetching}
|
||||||
|
name={'item-category-list'}
|
||||||
|
>
|
||||||
<ItemsCategoryActionsBar
|
<ItemsCategoryActionsBar
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
@@ -151,7 +170,7 @@ const ItemCategoryList = ({
|
|||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
onDeleteCategory={handleDeleteCategory}
|
onDeleteCategory={handleDeleteCategory}
|
||||||
loading={tableLoading}
|
loading={fetchCategories.isFetching}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
@@ -197,4 +216,5 @@ export default compose(
|
|||||||
withItemCategoriesActions,
|
withItemCategoriesActions,
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withDialogActions,
|
withDialogActions,
|
||||||
|
withResourceActions,
|
||||||
)(ItemCategoryList);
|
)(ItemCategoryList);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useMemo, useCallback,useEffect } from 'react';
|
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik, Formik } from 'formik';
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
@@ -23,29 +23,28 @@ import ErrorMessage from 'components/ErrorMessage';
|
|||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import MoneyInputGroup from 'components/MoneyInputGroup';
|
import MoneyInputGroup from 'components/MoneyInputGroup';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import { ListSelect } from 'components';
|
import { ListSelect, AccountsSelectList, If } from 'components';
|
||||||
|
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
import withItemCategories from 'containers/Items/withItemCategories'
|
import withItemCategories from 'containers/Items/withItemCategories';
|
||||||
import withAccounts from 'containers/Accounts/withAccounts';
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
import withItemDetail from 'containers/Items/withItemDetail'
|
import withItemDetail from 'containers/Items/withItemDetail';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
const ItemForm = ({
|
const ItemForm = ({
|
||||||
// #withItemActions
|
// #withItemActions
|
||||||
requestSubmitItem,
|
requestSubmitItem,
|
||||||
requestEditItem,
|
requestEditItem,
|
||||||
|
|
||||||
accounts,
|
accountsList,
|
||||||
itemDetail,
|
itemDetail,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onCancelForm,
|
onCancelForm,
|
||||||
|
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
@@ -55,10 +54,10 @@ const ItemForm = ({
|
|||||||
|
|
||||||
// #withMediaActions
|
// #withMediaActions
|
||||||
requestSubmitMedia,
|
requestSubmitMedia,
|
||||||
requestDeleteMedia,
|
requestDeleteMedia,
|
||||||
}) => {
|
}) => {
|
||||||
const [payload, setPayload] = useState({});
|
const [payload, setPayload] = useState({});
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const {
|
const {
|
||||||
@@ -72,22 +71,34 @@ const ItemForm = ({
|
|||||||
deleteCallback: requestDeleteMedia,
|
deleteCallback: requestDeleteMedia,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ItemTypeDisplay = useMemo(() => [
|
const ItemTypeDisplay = useMemo(
|
||||||
{ value: null, label: formatMessage({id:'select_item_type'}) },
|
() => [
|
||||||
{ value: 'service', label: formatMessage({id:'service'}) },
|
{ value: null, label: formatMessage({ id: 'select_item_type' }) },
|
||||||
{ value: 'inventory', label: formatMessage({id:'inventory'}) },
|
{ value: 'service', label: formatMessage({ id: 'service' }) },
|
||||||
{ value: 'non-inventory', label: formatMessage({id:'non_inventory'}) },
|
{ value: 'inventory', label: formatMessage({ id: 'inventory' }) },
|
||||||
], [formatMessage]);
|
{ value: 'non-inventory', label: formatMessage({ id: 'non_inventory' }) },
|
||||||
|
],
|
||||||
|
[formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
active: Yup.boolean(),
|
active: Yup.boolean(),
|
||||||
name: Yup.string().required().label(formatMessage({id:'item_name_'})),
|
name: Yup.string()
|
||||||
type: Yup.string().trim().required().label(formatMessage({id:'item_type_'})),
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_name_' })),
|
||||||
|
type: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_type_' })),
|
||||||
sku: Yup.string().trim(),
|
sku: Yup.string().trim(),
|
||||||
cost_price: Yup.number(),
|
cost_price: Yup.number(),
|
||||||
sell_price: Yup.number(),
|
sell_price: Yup.number(),
|
||||||
cost_account_id: Yup.number().required().label(formatMessage({id:'cost_account_id'})),
|
cost_account_id: Yup.number()
|
||||||
sell_account_id: Yup.number().required().label(formatMessage({id:'sell_account_id'})),
|
.required()
|
||||||
|
.label(formatMessage({ id: 'cost_account_id' })),
|
||||||
|
sell_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'sell_account_id' })),
|
||||||
inventory_account_id: Yup.number().when('type', {
|
inventory_account_id: Yup.number().when('type', {
|
||||||
is: (value) => value === 'inventory',
|
is: (value) => value === 'inventory',
|
||||||
then: Yup.number().required(),
|
then: Yup.number().required(),
|
||||||
@@ -95,6 +106,8 @@ const ItemForm = ({
|
|||||||
}),
|
}),
|
||||||
category_id: Yup.number().nullable(),
|
category_id: Yup.number().nullable(),
|
||||||
stock: Yup.string() || Yup.boolean(),
|
stock: Yup.string() || Yup.boolean(),
|
||||||
|
sellable: Yup.boolean().required(),
|
||||||
|
purchasable: Yup.boolean().required(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultInitialValues = useMemo(
|
const defaultInitialValues = useMemo(
|
||||||
@@ -110,27 +123,36 @@ const ItemForm = ({
|
|||||||
inventory_account_id: null,
|
inventory_account_id: null,
|
||||||
category_id: null,
|
category_id: null,
|
||||||
note: '',
|
note: '',
|
||||||
|
sellable: null,
|
||||||
|
purchasable: null,
|
||||||
}),
|
}),
|
||||||
[]
|
[],
|
||||||
|
);
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
...(itemDetail
|
||||||
|
? {
|
||||||
|
...pick(itemDetail, Object.keys(defaultInitialValues)),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...defaultInitialValues,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[itemDetail, defaultInitialValues],
|
||||||
);
|
);
|
||||||
const initialValues = useMemo(() => ({
|
|
||||||
...(itemDetail) ? {
|
|
||||||
...pick(itemDetail, Object.keys(defaultInitialValues)),
|
|
||||||
|
|
||||||
} : {
|
|
||||||
...defaultInitialValues,
|
|
||||||
}
|
|
||||||
}), [itemDetail, defaultInitialValues]);
|
|
||||||
|
|
||||||
const saveInvokeSubmit = useCallback((payload) => {
|
const saveInvokeSubmit = useCallback(
|
||||||
onFormSubmit && onFormSubmit(payload)
|
(payload) => {
|
||||||
}, [onFormSubmit]);
|
onFormSubmit && onFormSubmit(payload);
|
||||||
|
},
|
||||||
|
[onFormSubmit],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
itemDetail && itemDetail.id ?
|
itemDetail && itemDetail.id
|
||||||
changePageTitle(formatMessage({id:'edit_item_details'})) :
|
? changePageTitle(formatMessage({ id: 'edit_item_details' }))
|
||||||
changePageTitle(formatMessage({id:'new_item'}));
|
: changePageTitle(formatMessage({ id: 'new_item' }));
|
||||||
}, [changePageTitle,itemDetail,formatMessage]);
|
}, [changePageTitle, itemDetail, formatMessage]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getFieldProps,
|
getFieldProps,
|
||||||
@@ -146,55 +168,56 @@ const ItemForm = ({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
...initialValues,
|
...initialValues,
|
||||||
},
|
},
|
||||||
onSubmit: (values, { setSubmitting,resetForm,setErrors }) => {
|
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||||
|
|
||||||
const saveItem = (mediaIds) => {
|
const saveItem = (mediaIds) => {
|
||||||
const formValues = { ...values, media_ids: mediaIds };
|
const formValues = { ...values, media_ids: mediaIds };
|
||||||
if(itemDetail && itemDetail.id ){
|
if (itemDetail && itemDetail.id) {
|
||||||
|
requestEditItem(itemDetail.id, formValues)
|
||||||
requestEditItem(itemDetail.id,formValues)
|
.then((response) => {
|
||||||
.then((response)=>{
|
AppToaster.show({
|
||||||
AppToaster.show({
|
message: formatMessage(
|
||||||
message:formatMessage({
|
{
|
||||||
id:'the_item_has_been_successfully_edited',
|
id: 'the_item_has_been_successfully_edited',
|
||||||
},{
|
},
|
||||||
number:itemDetail.id
|
{
|
||||||
}),
|
number: itemDetail.id,
|
||||||
intent:Intent.SUCCESS
|
},
|
||||||
|
),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
saveInvokeSubmit({ action: 'update', ...payload });
|
||||||
|
history.push('/items');
|
||||||
|
resetForm();
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
} else {
|
||||||
saveInvokeSubmit({action:'update',...payload})
|
|
||||||
history.push('/items');
|
|
||||||
resetForm();
|
|
||||||
}).catch((errors)=>{
|
|
||||||
setSubmitting(false)
|
|
||||||
});
|
|
||||||
|
|
||||||
}else{
|
|
||||||
|
|
||||||
requestSubmitItem(formValues).then((response) => {
|
requestSubmitItem(formValues).then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage(
|
||||||
id: 'service_has_been_successful_created',
|
{
|
||||||
}, {
|
id: 'service_has_been_successful_created',
|
||||||
name: values.name,
|
},
|
||||||
service: formatMessage({ id: 'item' }),
|
{
|
||||||
}),
|
name: values.name,
|
||||||
|
service: formatMessage({ id: 'item' }),
|
||||||
|
},
|
||||||
|
),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
queryCache.removeQueries(['items-table']);
|
queryCache.removeQueries(['items-table']);
|
||||||
history.push('/items');
|
history.push('/items');
|
||||||
});
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Promise.all([saveMedia(), deleteMedia()]).then(
|
Promise.all([saveMedia(), deleteMedia()]).then(
|
||||||
([savedMediaResponses]) => {
|
([savedMediaResponses]) => {
|
||||||
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
||||||
return saveItem(mediaIds);
|
return saveItem(mediaIds);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -208,53 +231,52 @@ const ItemForm = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Filter Account Items
|
// Filter Account Items
|
||||||
const filterAccounts = (query, account, _index, exactMatch) => {
|
const filterAccounts = (query, item, _index, exactMatch) => {
|
||||||
const normalizedTitle = account.name.toLowerCase();
|
const normalizedTitle = item.name.toLowerCase();
|
||||||
const normalizedQuery = query.toLowerCase();
|
const normalizedQuery = query.toLowerCase();
|
||||||
if (exactMatch) {
|
if (exactMatch) {
|
||||||
return normalizedTitle === normalizedQuery;
|
return normalizedTitle === normalizedQuery;
|
||||||
} else {
|
} else {
|
||||||
return `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
return `${item.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onItemAccountSelect = useCallback((filedName) => {
|
const onItemAccountSelect = useCallback(
|
||||||
return (account) => {
|
(filedName) => {
|
||||||
setFieldValue(filedName, account.id);
|
return (account) => {
|
||||||
};
|
setFieldValue(filedName, account.id);
|
||||||
}, [setFieldValue]);
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
const categoryItem = useCallback(
|
const categoryItem = useCallback(
|
||||||
(item, { handleClick }) => (
|
(item, { handleClick }) => (
|
||||||
<MenuItem text={item.name} onClick={handleClick} />
|
<MenuItem key={item.id} text={item.name} onClick={handleClick} />
|
||||||
),
|
),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const requiredSpan = useMemo(() => <span class='required'>*</span>, []);
|
const requiredSpan = useMemo(() => <span class="required">*</span>, []);
|
||||||
const infoIcon = useMemo(() => <Icon icon='info-circle' iconSize={12} />, []);
|
const infoIcon = useMemo(() => <Icon icon="info-circle" iconSize={12} />, []);
|
||||||
|
|
||||||
const handleMoneyInputChange = (fieldKey) => (e, value) => {
|
const handleMoneyInputChange = (fieldKey) => (e, value) => {
|
||||||
setFieldValue(fieldKey, value);
|
setFieldValue(fieldKey, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
const initialAttachmentFiles =useMemo(()=>{
|
|
||||||
return itemDetail && itemDetail.media
|
return itemDetail && itemDetail.media
|
||||||
? itemDetail.media.map((attach)=>({
|
? itemDetail.media.map((attach) => ({
|
||||||
|
preview: attach.attachment_file,
|
||||||
preview:attach.attachment_file,
|
upload: true,
|
||||||
upload:true,
|
metadata: { ...attach },
|
||||||
metadata:{...attach}
|
}))
|
||||||
|
: [];
|
||||||
})):[];
|
}, [itemDetail]);
|
||||||
|
|
||||||
},[itemDetail])
|
|
||||||
const handleDropFiles = useCallback((_files) => {
|
const handleDropFiles = useCallback((_files) => {
|
||||||
setFiles(_files.filter((file) => file.uploaded === false));
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -267,7 +289,7 @@ const ItemForm = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setDeletedFiles, deletedFiles,]
|
[setDeletedFiles, deletedFiles],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCancelClickBtn = () => {
|
const handleCancelClickBtn = () => {
|
||||||
@@ -275,12 +297,11 @@ const ItemForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class='item-form'>
|
<div class="item-form">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div class='item-form__primary-section'>
|
<div class="item-form__primary-section">
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={7}>
|
<Col xs={7}>
|
||||||
|
|
||||||
{/* Item type */}
|
{/* Item type */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
medium={true}
|
medium={true}
|
||||||
@@ -289,7 +310,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-type'}
|
className={'form-group--item-type'}
|
||||||
intent={errors.type && touched.type && Intent.DANGER}
|
intent={errors.type && touched.type && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='type' />
|
<ErrorMessage {...{ errors, touched }} name="type" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -299,7 +320,7 @@ const ItemForm = ({
|
|||||||
{...getFieldProps('type')}
|
{...getFieldProps('type')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{/* Item name */}
|
{/* Item name */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'item_name'} />}
|
label={<T id={'item_name'} />}
|
||||||
@@ -307,7 +328,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-name'}
|
className={'form-group--item-name'}
|
||||||
intent={errors.name && touched.name && Intent.DANGER}
|
intent={errors.name && touched.name && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='name' />
|
<ErrorMessage {...{ errors, touched }} name="name" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -324,7 +345,9 @@ const ItemForm = ({
|
|||||||
labelInfo={infoIcon}
|
labelInfo={infoIcon}
|
||||||
className={'form-group--item-sku'}
|
className={'form-group--item-sku'}
|
||||||
intent={errors.sku && touched.sku && Intent.DANGER}
|
intent={errors.sku && touched.sku && Intent.DANGER}
|
||||||
helperText={<ErrorMessage {...{ errors, touched }} name='sku' />}
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name="sku" />
|
||||||
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
@@ -343,29 +366,28 @@ const ItemForm = ({
|
|||||||
errors.category_id && touched.category_id && Intent.DANGER
|
errors.category_id && touched.category_id && Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='category' />
|
<ErrorMessage {...{ errors, touched }} name="category" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
'form-group--category',
|
'form-group--category',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
items={categoriesList}
|
items={categoriesList}
|
||||||
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
itemRenderer={categoryItem}
|
itemRenderer={categoryItem}
|
||||||
itemPredicate={filterAccounts}
|
itemPredicate={filterAccounts}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
onItemSelect={onItemAccountSelect('category_id')}
|
onItemSelect={onItemAccountSelect('category_id')}
|
||||||
|
selectedItem={values.customer_id}
|
||||||
selectedItem={values.category_id}
|
|
||||||
selectedItemProp={'id'}
|
selectedItemProp={'id'}
|
||||||
|
|
||||||
defaultText={<T id={'select_category'} />}
|
defaultText={<T id={'select_category'} />}
|
||||||
labelProp={'name'}
|
labelProp={'name'}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{/* Active checkbox */}
|
{/* Active checkbox */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={' '}
|
label={' '}
|
||||||
@@ -374,7 +396,7 @@ const ItemForm = ({
|
|||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline={true}
|
inline={true}
|
||||||
label={<T id={'active'}/>}
|
label={<T id={'active'} />}
|
||||||
defaultChecked={values.active}
|
defaultChecked={values.active}
|
||||||
{...getFieldProps('active')}
|
{...getFieldProps('active')}
|
||||||
/>
|
/>
|
||||||
@@ -395,14 +417,18 @@ const ItemForm = ({
|
|||||||
|
|
||||||
<Row gutterWidth={16} className={'item-form__accounts-section'}>
|
<Row gutterWidth={16} className={'item-form__accounts-section'}>
|
||||||
<Col width={404}>
|
<Col width={404}>
|
||||||
<h4><T id={'purchase_information'}/></h4>
|
<h4>
|
||||||
|
<T id={'sales_information'} />
|
||||||
|
</h4>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'selling_price'}/>}
|
label={<T id={'selling_price'} />}
|
||||||
className={'form-group--item-selling-price'}
|
className={'form-group--item-selling-price'}
|
||||||
intent={errors.selling_price && touched.selling_price && Intent.DANGER}
|
intent={
|
||||||
|
errors.selling_price && touched.selling_price && Intent.DANGER
|
||||||
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='selling_price' />
|
<ErrorMessage {...{ errors, touched }} name="selling_price" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -417,9 +443,10 @@ const ItemForm = ({
|
|||||||
touched.selling_price &&
|
touched.selling_price &&
|
||||||
Intent.DANGER,
|
Intent.DANGER,
|
||||||
}}
|
}}
|
||||||
|
disabled={!values.sellable}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{/* Selling account */}
|
{/* Selling account */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'account'} />}
|
label={<T id={'account'} />}
|
||||||
@@ -431,32 +458,42 @@ const ItemForm = ({
|
|||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='sell_account_id' />
|
<ErrorMessage {...{ errors, touched }} name="sell_account_id" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--sell-account',
|
'form-group--sell-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<AccountsSelectList
|
||||||
items={accounts}
|
accounts={accountsList}
|
||||||
itemRenderer={accountItem}
|
onAccountSelected={onItemAccountSelect('sell_account_id')}
|
||||||
itemPredicate={filterAccounts}
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
popoverProps={{ minimal: true }}
|
selectedAccountId={values.sell_account_id}
|
||||||
onItemSelect={onItemAccountSelect('sell_account_id')}
|
disabled={!values.sellable}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
selectedItem={values.sell_account_id}
|
{/* sellable checkbox */}
|
||||||
selectedItemProp={'id'}
|
<FormGroup
|
||||||
|
label={' '}
|
||||||
defaultText={<T id={'select_account'} />}
|
inline={true}
|
||||||
labelProp={'name'}
|
className={'form-group--sellable'}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
label={<T id={'sellable'} />}
|
||||||
|
checked={values.sellable}
|
||||||
|
{...getFieldProps('sellable')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col width={404}>
|
<Col width={404}>
|
||||||
<h4><T id={'sales_information'} /></h4>
|
<h4>
|
||||||
|
<T id={'purchase_information'} />
|
||||||
|
</h4>
|
||||||
|
|
||||||
{/* Cost price */}
|
{/* Cost price */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -464,7 +501,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-cost-price'}
|
className={'form-group--item-cost-price'}
|
||||||
intent={errors.cost_price && touched.cost_price && Intent.DANGER}
|
intent={errors.cost_price && touched.cost_price && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='cost_price' />
|
<ErrorMessage {...{ errors, touched }} name="cost_price" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -477,6 +514,7 @@ const ItemForm = ({
|
|||||||
intent:
|
intent:
|
||||||
errors.cost_price && touched.cost_price && Intent.DANGER,
|
errors.cost_price && touched.cost_price && Intent.DANGER,
|
||||||
}}
|
}}
|
||||||
|
disabled={!values.purchasable}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@@ -490,25 +528,34 @@ const ItemForm = ({
|
|||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='cost_account_id' />
|
<ErrorMessage {...{ errors, touched }} name="cost_account_id" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--cost-account',
|
'form-group--cost-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<AccountsSelectList
|
||||||
items={accounts}
|
accounts={accountsList}
|
||||||
itemRenderer={accountItem}
|
onAccountSelected={onItemAccountSelect('cost_account_id')}
|
||||||
itemPredicate={filterAccounts}
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
popoverProps={{ minimal: true }}
|
selectedAccountId={values.cost_account_id}
|
||||||
onItemSelect={onItemAccountSelect('cost_account_id')}
|
disabled={!values.purchasable}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
defaultText={<T id={'select_account'} />}
|
{/* purchasable checkbox */}
|
||||||
labelProp={'name'}
|
<FormGroup
|
||||||
selectedItem={values.cost_account_id}
|
label={' '}
|
||||||
selectedItemProp={'id'}
|
inline={true}
|
||||||
|
className={'form-group--purchasable'}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
label={<T id={'purchasable'} />}
|
||||||
|
defaultChecked={values.purchasable}
|
||||||
|
{...getFieldProps('purchasable')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -521,35 +568,35 @@ const ItemForm = ({
|
|||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'inventory_account'}/>}
|
label={<T id={'inventory_account'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
intent={
|
intent={
|
||||||
errors.inventory_account_id &&
|
errors.inventory_account_id &&
|
||||||
touched.inventory_account_id &&
|
touched.inventory_account_id &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={<ErrorMessage {...{ errors, touched }} name='inventory_account_id' />}
|
helperText={
|
||||||
|
<ErrorMessage
|
||||||
|
{...{ errors, touched }}
|
||||||
|
name="inventory_account_id"
|
||||||
|
/>
|
||||||
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--item-inventory_account',
|
'form-group--item-inventory_account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<AccountsSelectList
|
||||||
items={accounts}
|
accounts={accountsList}
|
||||||
itemRenderer={accountItem}
|
onAccountSelected={onItemAccountSelect('inventory_account_id')}
|
||||||
itemPredicate={filterAccounts}
|
defaultSelectText={<T id={'select_account'} />}
|
||||||
popoverProps={{ minimal: true }}
|
selectedAccountId={values.inventory_account_id}
|
||||||
onItemSelect={onItemAccountSelect('inventory_account_id')}
|
/>
|
||||||
|
|
||||||
defaultText={<T id={'select_account'} />}
|
|
||||||
labelProp={'name'}
|
|
||||||
selectedItem={values.inventory_account_id}
|
|
||||||
selectedItemProp={'id'} />
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'opening_stock'}/>}
|
label={<T id={'opening_stock'} />}
|
||||||
className={'form-group--item-stock'}
|
className={'form-group--item-stock'}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -562,14 +609,17 @@ const ItemForm = ({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<div class='form__floating-footer'>
|
<div class="form__floating-footer">
|
||||||
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type='submit'>
|
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type="submit">
|
||||||
|
{itemDetail && itemDetail.id ? (
|
||||||
{ itemDetail && itemDetail.id ? <T id={'edit'}/> : <T id={'save'}/> }
|
<T id={'edit'} />
|
||||||
|
) : (
|
||||||
|
<T id={'save'} />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button className={'ml1'} disabled={isSubmitting}>
|
<Button className={'ml1'} disabled={isSubmitting}>
|
||||||
<T id={'save_as_draft'}/>
|
<T id={'save_as_draft'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button className={'ml1'} onClick={handleCancelClickBtn}>
|
<Button className={'ml1'} onClick={handleCancelClickBtn}>
|
||||||
@@ -582,8 +632,8 @@ const ItemForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withAccounts(({accounts})=>({
|
withAccounts(({ accountsList }) => ({
|
||||||
accounts,
|
accountsList,
|
||||||
})),
|
})),
|
||||||
withAccountDetail,
|
withAccountDetail,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ import { If } from 'components';
|
|||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
|
import withItemsActions from './withItemsActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
const ItemsActionsBar = ({
|
const ItemsActionsBar = ({
|
||||||
openDialog,
|
openDialog,
|
||||||
@@ -36,6 +37,9 @@ const ItemsActionsBar = ({
|
|||||||
// #withItems
|
// #withItems
|
||||||
itemsViews,
|
itemsViews,
|
||||||
|
|
||||||
|
//#withItemActions
|
||||||
|
addItemsTableQueries,
|
||||||
|
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
selectedRows = [],
|
selectedRows = [],
|
||||||
onBulkDelete,
|
onBulkDelete,
|
||||||
@@ -56,10 +60,26 @@ const ItemsActionsBar = ({
|
|||||||
selectedRows,
|
selectedRows,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// name
|
||||||
|
// const filterDropdown = FilterDropdown({
|
||||||
|
// fields: resourceFields,
|
||||||
|
// onFilterChange: (filterConditions) => {
|
||||||
|
// setFilterCount(filterConditions.length);
|
||||||
|
// onFilterChanged && onFilterChanged(filterConditions);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
const filterDropdown = FilterDropdown({
|
const filterDropdown = FilterDropdown({
|
||||||
|
initialCondition: {
|
||||||
|
fieldKey: 'name',
|
||||||
|
compatator: 'contains',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
fields: resourceFields,
|
fields: resourceFields,
|
||||||
onFilterChange: (filterConditions) => {
|
onFilterChange: (filterConditions) => {
|
||||||
setFilterCount(filterConditions.length);
|
addItemsTableQueries({
|
||||||
|
filter_roles: filterConditions || '',
|
||||||
|
});
|
||||||
onFilterChanged && onFilterChanged(filterConditions);
|
onFilterChanged && onFilterChanged(filterConditions);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -107,9 +127,11 @@ const ItemsActionsBar = ({
|
|||||||
<Button
|
<Button
|
||||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||||
text={
|
text={
|
||||||
filterCount <= 0 ?
|
filterCount <= 0 ? (
|
||||||
(<T id={'filter'} />) :
|
<T id={'filter'} />
|
||||||
(`${filterCount} ${formatMessage({ id: 'filters_applied' })}`)
|
) : (
|
||||||
|
`${filterCount} ${formatMessage({ id: 'filters_applied' })}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||||
/>
|
/>
|
||||||
@@ -140,7 +162,14 @@ const ItemsActionsBar = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
resourceName: 'items',
|
||||||
|
});
|
||||||
|
|
||||||
|
const withItemsActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
withItemsActionsBar,
|
||||||
withDialogActions,
|
withDialogActions,
|
||||||
withItems(({ itemsViews }) => ({
|
withItems(({ itemsViews }) => ({
|
||||||
itemsViews,
|
itemsViews,
|
||||||
@@ -148,4 +177,5 @@ export default compose(
|
|||||||
withResourceDetail(({ resourceFields }) => ({
|
withResourceDetail(({ resourceFields }) => ({
|
||||||
resourceFields,
|
resourceFields,
|
||||||
})),
|
})),
|
||||||
|
withItemsActions,
|
||||||
)(ItemsActionsBar);
|
)(ItemsActionsBar);
|
||||||
|
|||||||
@@ -12,16 +12,18 @@ import {
|
|||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { If, Icon } from 'components';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
|
||||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import { If } from 'components';
|
|
||||||
|
|
||||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
|
import withItemCategories from './withItemCategories';
|
||||||
|
import withItemCategoriesActions from './withItemCategoriesActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
const ItemsCategoryActionsBar = ({
|
const ItemsCategoryActionsBar = ({
|
||||||
@@ -31,6 +33,12 @@ const ItemsCategoryActionsBar = ({
|
|||||||
// #withDialog
|
// #withDialog
|
||||||
openDialog,
|
openDialog,
|
||||||
|
|
||||||
|
// #withItemCategories
|
||||||
|
categoriesViews,
|
||||||
|
|
||||||
|
// #withItemCategoriesActions
|
||||||
|
addItemCategoriesTableQueries,
|
||||||
|
|
||||||
// #ownProps
|
// #ownProps
|
||||||
selectedRows = [],
|
selectedRows = [],
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
@@ -46,17 +54,21 @@ const ItemsCategoryActionsBar = ({
|
|||||||
selectedRows,
|
selectedRows,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// const handleDeleteCategory = useCallback((category) => {
|
// const filterDropdown = FilterDropdown({
|
||||||
// onDeleteCategory(selectedRows);
|
// fields: resourceFields,
|
||||||
// }, [selectedRows, onDeleteCategory]);
|
// initialCondition: {
|
||||||
|
// fieldKey: 'name',
|
||||||
const filterDropdown = FilterDropdown({
|
// compatator: 'contains',
|
||||||
fields: resourceFields,
|
// value: '',
|
||||||
onFilterChange: (filterConditions) => {
|
// },
|
||||||
setFilterCount(filterConditions.length || 0);
|
// onFilterChange: (filterConditions) => {
|
||||||
onFilterChanged && onFilterChanged(filterConditions);
|
// setFilterCount(filterConditions.length || 0);
|
||||||
},
|
// addItemCategoriesTableQueries({
|
||||||
});
|
// filter_roles: filterConditions || '',
|
||||||
|
// });
|
||||||
|
// onFilterChanged && onFilterChanged(filterConditions);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
const handelBulkDelete = useCallback(() => {
|
const handelBulkDelete = useCallback(() => {
|
||||||
onBulkDelete && onBulkDelete(selectedRows.map((r) => r.id));
|
onBulkDelete && onBulkDelete(selectedRows.map((r) => r.id));
|
||||||
@@ -75,22 +87,29 @@ const ItemsCategoryActionsBar = ({
|
|||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
minimal={true}
|
minimal={true}
|
||||||
content={filterDropdown}
|
// content={filterDropdown}
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
position={Position.BOTTOM_LEFT}
|
position={Position.BOTTOM_LEFT}
|
||||||
|
canOutsideClickClose={true}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||||
text={ filterCount <= 0 ? <T id={'filter'}/> : `${filterCount} filters applied`}
|
text={
|
||||||
icon={<Icon icon='filter-16' iconSize={16} />}
|
filterCount <= 0 ? (
|
||||||
|
<T id={'filter'} />
|
||||||
|
) : (
|
||||||
|
`${filterCount} filters applied`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<If condition={hasSelectedRows}>
|
<If condition={hasSelectedRows}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='trash-16' iconSize={16} />}
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
text={<T id={'delete'}/>}
|
text={<T id={'delete'} />}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={handelBulkDelete}
|
onClick={handelBulkDelete}
|
||||||
/>
|
/>
|
||||||
@@ -98,13 +117,13 @@ const ItemsCategoryActionsBar = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='file-import-16' iconSize={16} />}
|
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||||
text={<T id={'import'}/>}
|
text={<T id={'import'} />}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='file-export-16' iconSize={16} />}
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
text={<T id={'export'}/>}
|
text={<T id={'export'} />}
|
||||||
/>
|
/>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
@@ -114,7 +133,6 @@ const ItemsCategoryActionsBar = ({
|
|||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
resourceName: 'items_categories',
|
resourceName: 'items_categories',
|
||||||
});
|
});
|
||||||
|
|
||||||
const withItemsCategoriesActionsBar = connect(mapStateToProps);
|
const withItemsCategoriesActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
@@ -124,4 +142,8 @@ export default compose(
|
|||||||
withResourceDetail(({ resourceFields }) => ({
|
withResourceDetail(({ resourceFields }) => ({
|
||||||
resourceFields,
|
resourceFields,
|
||||||
})),
|
})),
|
||||||
|
// withItemCategories(({ categoriesViews }) => ({
|
||||||
|
// categoriesViews,
|
||||||
|
// })),
|
||||||
|
withItemCategoriesActions,
|
||||||
)(ItemsCategoryActionsBar);
|
)(ItemsCategoryActionsBar);
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
import React, { useEffect, useCallback, useState,useMemo } from 'react';
|
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||||
import {
|
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||||
Route,
|
import { Intent, Alert } from '@blueprintjs/core';
|
||||||
Switch,
|
|
||||||
useHistory
|
|
||||||
} from 'react-router-dom';
|
|
||||||
import {
|
|
||||||
Intent,
|
|
||||||
Alert,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { FormattedMessage as T, FormattedHTMLMessage, useIntl } from 'react-intl';
|
import {
|
||||||
|
FormattedMessage as T,
|
||||||
|
FormattedHTMLMessage,
|
||||||
|
useIntl,
|
||||||
|
} from 'react-intl';
|
||||||
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import ItemsActionsBar from 'containers/Items/ItemsActionsBar';
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
import ItemsDataTable from './ItemsDataTable';
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
|
||||||
|
|
||||||
import ItemsViewsTabs from 'containers/Items/ItemsViewsTabs';
|
import ItemsViewsTabs from 'containers/Items/ItemsViewsTabs';
|
||||||
|
import ItemsDataTable from './ItemsDataTable';
|
||||||
|
import ItemsActionsBar from 'containers/Items/ItemsActionsBar';
|
||||||
|
|
||||||
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
@@ -27,7 +24,6 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
import withViewsActions from 'containers/Views/withViewsActions';
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
|
||||||
|
|
||||||
function ItemsList({
|
function ItemsList({
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
@@ -51,37 +47,41 @@ function ItemsList({
|
|||||||
const [bulkDelete, setBulkDelete] = useState(false);
|
const [bulkDelete, setBulkDelete] = useState(false);
|
||||||
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePageTitle(formatMessage({id:'items_list'}));
|
changePageTitle(formatMessage({ id: 'items_list' }));
|
||||||
}, [changePageTitle]);
|
}, [changePageTitle]);
|
||||||
|
|
||||||
const fetchHook = useQuery('items-resource', () => {
|
const fetchResourceViews = useQuery(
|
||||||
return Promise.all([
|
['resource-views', 'items'],
|
||||||
requestFetchResourceViews('items'),
|
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||||
requestFetchResourceFields('items'),
|
);
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetchItems = useQuery(['items-table', itemsTableQuery],
|
const fetchResourceFields = useQuery(
|
||||||
() => requestFetchItems({}));
|
['resource-fields', 'items'],
|
||||||
|
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchItems = useQuery(['items-table', itemsTableQuery], () =>
|
||||||
|
requestFetchItems({}),
|
||||||
|
);
|
||||||
|
|
||||||
// Handle click delete item.
|
// Handle click delete item.
|
||||||
const handleDeleteItem = useCallback((item) => {
|
const handleDeleteItem = useCallback(
|
||||||
setDeleteItem(item);
|
(item) => {
|
||||||
}, [setDeleteItem]);
|
setDeleteItem(item);
|
||||||
|
},
|
||||||
|
[setDeleteItem],
|
||||||
|
);
|
||||||
|
|
||||||
const handleEditItem = useCallback(
|
const handleEditItem = useCallback(
|
||||||
(item) => {
|
(item) => {
|
||||||
history.push(`/items/${item.id}/edit`);
|
history.push(`/items/${item.id}/edit`);
|
||||||
},
|
},
|
||||||
[history]
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle cancel delete the item.
|
// Handle cancel delete the item.
|
||||||
const handleCancelDeleteItem = useCallback(() => {
|
const handleCancelDeleteItem = useCallback(() => {
|
||||||
setDeleteItem(false);
|
setDeleteItem(false);
|
||||||
@@ -98,29 +98,43 @@ function ItemsList({
|
|||||||
});
|
});
|
||||||
setDeleteItem(false);
|
setDeleteItem(false);
|
||||||
});
|
});
|
||||||
}, [requestDeleteItem, deleteItem,formatMessage]);
|
}, [requestDeleteItem, deleteItem, formatMessage]);
|
||||||
|
|
||||||
// Handle fetch data table.
|
const handleFetchData = useCallback(
|
||||||
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
({ pageIndex, pageSize, sortBy }) => {
|
||||||
addItemsTableQueries({
|
const page = pageIndex + 1;
|
||||||
...(sortBy.length > 0) ? {
|
|
||||||
column_sort_order: sortBy[0].id,
|
addItemsTableQueries({
|
||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
...(sortBy.length > 0
|
||||||
} : {},
|
? {
|
||||||
});
|
column_sort_by: sortBy[0].id,
|
||||||
}, [fetchItems, addItemsTableQueries]);
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
page_size: pageSize,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addItemsTableQueries],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle filter change to re-fetch the items.
|
// Handle filter change to re-fetch the items.
|
||||||
const handleFilterChanged = useCallback((filterConditions) => {
|
const handleFilterChanged = useCallback(
|
||||||
addItemsTableQueries({
|
(filterConditions) => {
|
||||||
filter_roles: filterConditions || '',
|
addItemsTableQueries({
|
||||||
});
|
filter_roles: filterConditions || '',
|
||||||
}, [fetchItems]);
|
});
|
||||||
|
},
|
||||||
|
[fetchItems],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle custom view change to re-fetch the items.
|
// Handle custom view change to re-fetch the items.
|
||||||
const handleCustomViewChanged = useCallback((customViewId) => {
|
const handleCustomViewChanged = useCallback(
|
||||||
setTableLoading(true);
|
(customViewId) => {
|
||||||
}, [fetchItems]);
|
setTableLoading(true);
|
||||||
|
},
|
||||||
|
[fetchItems],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tableLoading && !fetchItems.isFetching) {
|
if (tableLoading && !fetchItems.isFetching) {
|
||||||
@@ -129,108 +143,124 @@ function ItemsList({
|
|||||||
}, [tableLoading, fetchItems.isFetching]);
|
}, [tableLoading, fetchItems.isFetching]);
|
||||||
|
|
||||||
// Handle selected rows change.
|
// Handle selected rows change.
|
||||||
const handleSelectedRowsChange = useCallback((accounts) => {
|
const handleSelectedRowsChange = useCallback(
|
||||||
setSelectedRows(accounts);
|
(accounts) => {
|
||||||
}, [setSelectedRows]);
|
setSelectedRows(accounts);
|
||||||
|
},
|
||||||
// Calculates the data table selected rows count.
|
[setSelectedRows],
|
||||||
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [selectedRows]);
|
);
|
||||||
|
|
||||||
|
// Calculates the data table selected rows count.
|
||||||
|
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
|
||||||
|
selectedRows,
|
||||||
|
]);
|
||||||
|
|
||||||
// Handle items bulk delete button click.,
|
// Handle items bulk delete button click.,
|
||||||
|
|
||||||
const handleBulkDelete = useCallback((itemsIds) => {
|
const handleBulkDelete = useCallback(
|
||||||
setBulkDelete(itemsIds);
|
(itemsIds) => {
|
||||||
}, [setBulkDelete]);
|
setBulkDelete(itemsIds);
|
||||||
|
},
|
||||||
|
[setBulkDelete],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle confirm items bulk delete.
|
// Handle confirm items bulk delete.
|
||||||
const handleConfirmBulkDelete = useCallback(() => {
|
const handleConfirmBulkDelete = useCallback(() => {
|
||||||
requestDeleteBulkItems(bulkDelete).then(() => {
|
requestDeleteBulkItems(bulkDelete)
|
||||||
setBulkDelete(false);
|
.then(() => {
|
||||||
AppToaster.show({
|
setBulkDelete(false);
|
||||||
message: formatMessage({ id: 'the_items_has_been_successfully_deleted' }),
|
AppToaster.show({
|
||||||
intent: Intent.SUCCESS,
|
message: formatMessage({
|
||||||
|
id: 'the_items_has_been_successfully_deleted',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setBulkDelete(false);
|
||||||
});
|
});
|
||||||
}).catch((errors) => {
|
}, [requestDeleteBulkItems, bulkDelete, formatMessage]);
|
||||||
setBulkDelete(false);
|
|
||||||
});
|
|
||||||
}, [requestDeleteBulkItems, bulkDelete,formatMessage]);
|
|
||||||
|
|
||||||
// Handle cancel accounts bulk delete.
|
|
||||||
const handleCancelBulkDelete = useCallback(() => {
|
|
||||||
setBulkDelete(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
// Handle cancel accounts bulk delete.
|
||||||
|
const handleCancelBulkDelete = useCallback(() => {
|
||||||
|
setBulkDelete(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
isLoading={fetchHook.isFetching}
|
loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
|
||||||
name={'items-list'}>
|
// isLoading={fetchHook.isFetching}
|
||||||
|
|
||||||
|
name={'items-list'}
|
||||||
|
>
|
||||||
<ItemsActionsBar
|
<ItemsActionsBar
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
onBulkDelete={handleBulkDelete}/>
|
onBulkDelete={handleBulkDelete}
|
||||||
|
/>
|
||||||
|
|
||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
exact={true}
|
exact={true}
|
||||||
path={[
|
path={['/items/:custom_view_id/custom_view', '/items']}
|
||||||
'/items/:custom_view_id/custom_view',
|
>
|
||||||
'/items'
|
<ItemsViewsTabs onViewChanged={handleCustomViewChanged} />
|
||||||
]}>
|
|
||||||
<ItemsViewsTabs
|
|
||||||
onViewChanged={handleCustomViewChanged} />
|
|
||||||
|
|
||||||
<ItemsDataTable
|
<ItemsDataTable
|
||||||
loading={tableLoading}
|
loading={fetchItems.isFetching}
|
||||||
onDeleteItem={handleDeleteItem}
|
onDeleteItem={handleDeleteItem}
|
||||||
onEditItem={handleEditItem}
|
onEditItem={handleEditItem}
|
||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange} />
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
/>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
cancelButtonText={<T id={'cancel'}/>}
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
confirmButtonText={<T id={'delete'}/>}
|
confirmButtonText={<T id={'delete'} />}
|
||||||
icon="trash"
|
icon="trash"
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
isOpen={deleteItem}
|
isOpen={deleteItem}
|
||||||
onCancel={handleCancelDeleteItem}
|
onCancel={handleCancelDeleteItem}
|
||||||
onConfirm={handleConfirmDeleteItem}>
|
onConfirm={handleConfirmDeleteItem}
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
<FormattedHTMLMessage
|
<FormattedHTMLMessage
|
||||||
id={'once_delete_this_item_you_will_able_to_restore_it'} />
|
id={'once_delete_this_item_you_will_able_to_restore_it'}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
cancelButtonText={<T id={'cancel'}/>}
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
confirmButtonText={`${formatMessage({id:'delete'})} (${selectedRowsCount})`}
|
confirmButtonText={`${formatMessage({
|
||||||
icon="trash"
|
id: 'delete',
|
||||||
intent={Intent.DANGER}
|
})} (${selectedRowsCount})`}
|
||||||
isOpen={bulkDelete}
|
icon="trash"
|
||||||
onCancel={handleCancelBulkDelete}
|
intent={Intent.DANGER}
|
||||||
onConfirm={handleConfirmBulkDelete}
|
isOpen={bulkDelete}
|
||||||
>
|
onCancel={handleCancelBulkDelete}
|
||||||
<p>
|
onConfirm={handleConfirmBulkDelete}
|
||||||
<T id={'once_delete_these_items_you_will_not_able_restore_them'} />
|
>
|
||||||
</p>
|
<p>
|
||||||
</Alert>
|
<T
|
||||||
|
id={'once_delete_these_items_you_will_not_able_restore_them'}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</DashboardPageContent>
|
</DashboardPageContent>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withItems(({ itemsTableQuery }) => ({
|
|
||||||
itemsTableQuery,
|
|
||||||
})),
|
|
||||||
withResourceActions,
|
withResourceActions,
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
withViewsActions,
|
withViewsActions,
|
||||||
|
withItems(({ itemsTableQuery }) => ({
|
||||||
|
itemsTableQuery,
|
||||||
|
})),
|
||||||
)(ItemsList);
|
)(ItemsList);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useEffect} from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
@@ -7,20 +7,22 @@ import {
|
|||||||
NavbarGroup,
|
NavbarGroup,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
Button
|
Button,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { Link, withRouter } from 'react-router-dom';
|
import { Link, withRouter } from 'react-router-dom';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import {useUpdateEffect} from 'hooks';
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
import { DashboardViewsTabs } from 'components';
|
||||||
|
import { pick, debounce } from 'lodash';
|
||||||
|
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withViewDetail from 'containers/Views/withViewDetails';
|
import withViewDetail from 'containers/Views/withViewDetails';
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
|
|
||||||
import { FormattedMessage as T} from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
function ItemsViewsTabs({
|
function ItemsViewsTabs({
|
||||||
// #withViewDetail
|
// #withViewDetail
|
||||||
@@ -42,7 +44,7 @@ function ItemsViewsTabs({
|
|||||||
onViewChanged,
|
onViewChanged,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { custom_view_id: customViewId } = useParams();
|
const { custom_view_id: customViewId = null } = useParams();
|
||||||
|
|
||||||
const handleClickNewView = () => {
|
const handleClickNewView = () => {
|
||||||
setTopbarEditView(null);
|
setTopbarEditView(null);
|
||||||
@@ -51,12 +53,12 @@ function ItemsViewsTabs({
|
|||||||
|
|
||||||
const handleViewLinkClick = () => {
|
const handleViewLinkClick = () => {
|
||||||
setTopbarEditView(customViewId);
|
setTopbarEditView(customViewId);
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changeItemsCurrentView(customViewId || -1);
|
changeItemsCurrentView(customViewId || -1);
|
||||||
setTopbarEditView(customViewId);
|
setTopbarEditView(customViewId);
|
||||||
changePageSubtitle((customViewId && viewItem) ? viewItem.name : '');
|
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||||
|
|
||||||
addItemsTableQueries({
|
addItemsTableQueries({
|
||||||
custom_view_id: customViewId || null,
|
custom_view_id: customViewId || null,
|
||||||
@@ -67,45 +69,37 @@ function ItemsViewsTabs({
|
|||||||
changeItemsCurrentView(-1);
|
changeItemsCurrentView(-1);
|
||||||
changePageSubtitle('');
|
changePageSubtitle('');
|
||||||
};
|
};
|
||||||
}, [customViewId]);
|
}, [customViewId, addItemsTableQueries, changeItemsCurrentView]);
|
||||||
|
|
||||||
useUpdateEffect(() => {
|
useUpdateEffect(() => {
|
||||||
onViewChanged && onViewChanged(customViewId);
|
onViewChanged && onViewChanged(customViewId);
|
||||||
}, [customViewId]);
|
}, [customViewId]);
|
||||||
|
|
||||||
const tabs = itemsViews.map(view => {
|
const debounceChangeHistory = useRef(
|
||||||
const baseUrl = '/items';
|
debounce((toUrl) => {
|
||||||
const link = (
|
history.push(toUrl);
|
||||||
<Link to={`${baseUrl}/${view.id}/custom_view`} onClick={handleViewLinkClick}>
|
}, 250),
|
||||||
{view.name}
|
);
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
return (<Tab id={`custom_view_${view.id}`} title={link} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const handleTabsChange = (viewId) => {
|
||||||
|
const toPath = viewId ? `${viewId}/custom_view` : '';
|
||||||
|
debounceChangeHistory.current(`/items/${toPath}`);
|
||||||
|
setTopbarEditView(viewId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabs = itemsViews.map((view) => ({
|
||||||
|
...pick(view, ['name', 'id']),
|
||||||
|
}));
|
||||||
return (
|
return (
|
||||||
<Navbar className='navbar--dashboard-views'>
|
<Navbar className="navbar--dashboard-views">
|
||||||
<NavbarGroup align={Alignment.LEFT}>
|
<NavbarGroup align={Alignment.LEFT}>
|
||||||
<Tabs
|
<DashboardViewsTabs
|
||||||
id='navbar'
|
initialViewId={customViewId}
|
||||||
large={true}
|
baseUrl={'/items'}
|
||||||
selectedTabId={customViewId ? `custom_view_${customViewId}` : 'all'}
|
tabs={tabs}
|
||||||
className='tabs--dashboard-views'
|
onNewViewTabClick={handleClickNewView}
|
||||||
>
|
onChange={handleTabsChange}
|
||||||
<Tab
|
/>
|
||||||
id='all'
|
|
||||||
title={<Link to={`/items`}><T id={'all'}/></Link>}
|
|
||||||
onClick={handleViewLinkClick} />
|
|
||||||
|
|
||||||
{tabs}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className='button--new-view'
|
|
||||||
icon={<Icon icon='plus' />}
|
|
||||||
onClick={handleClickNewView}
|
|
||||||
minimal={true}
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
);
|
||||||
@@ -123,8 +117,8 @@ export default compose(
|
|||||||
withItemsViewsTabs,
|
withItemsViewsTabs,
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
withViewDetail,
|
withViewDetail(),
|
||||||
withItems( ({ itemsViews }) => ({
|
withItems(({ itemsViews }) => ({
|
||||||
itemsViews,
|
itemsViews,
|
||||||
}))
|
})),
|
||||||
)(ItemsViewsTabs);
|
)(ItemsViewsTabs);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { getItemsCategoriesListFactory } from 'store/itemCategories/ItemsCategories.selectors';
|
||||||
getItemsCategoriesListFactory
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
} from 'store/itemCategories/ItemsCategories.selectors';
|
|
||||||
|
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getItemsCategoriesList = getItemsCategoriesListFactory();
|
const getItemsCategoriesList = getItemsCategoriesListFactory();
|
||||||
@@ -10,10 +8,11 @@ export default (mapState) => {
|
|||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const mapped = {
|
const mapped = {
|
||||||
categoriesList: getItemsCategoriesList(state, props),
|
categoriesList: getItemsCategoriesList(state, props),
|
||||||
|
itemCategoriesViews: getResourceViews(state, props, 'items_categories'),
|
||||||
categoriesTableLoading: state.itemCategories.loading,
|
categoriesTableLoading: state.itemCategories.loading,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapState;
|
return mapState ? mapState(mapped, state, props) : mapState;
|
||||||
};
|
};
|
||||||
|
|
||||||
return connect(mapStateToProps);
|
return connect(mapStateToProps);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,16 +4,30 @@ import {
|
|||||||
submitItemCategory,
|
submitItemCategory,
|
||||||
deleteItemCategory,
|
deleteItemCategory,
|
||||||
editItemCategory,
|
editItemCategory,
|
||||||
deleteBulkItemCategories
|
deleteBulkItemCategories,
|
||||||
|
|
||||||
} from 'store/itemCategories/itemsCategory.actions';
|
} from 'store/itemCategories/itemsCategory.actions';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
requestSubmitItemCategory: (form) => dispatch(submitItemCategory({ form })),
|
requestSubmitItemCategory: (form) => dispatch(submitItemCategory({ form })),
|
||||||
requestFetchItemCategories: (query) => dispatch(fetchItemCategories({ query })),
|
requestFetchItemCategories: (query) =>
|
||||||
|
dispatch(fetchItemCategories({ query })),
|
||||||
requestDeleteItemCategory: (id) => dispatch(deleteItemCategory(id)),
|
requestDeleteItemCategory: (id) => dispatch(deleteItemCategory(id)),
|
||||||
requestEditItemCategory: (id, form) => dispatch(editItemCategory(id, form)),
|
requestEditItemCategory: (id, form) => dispatch(editItemCategory(id, form)),
|
||||||
requestDeleteBulkItemCategories:(ids)=>dispatch(deleteBulkItemCategories({ids}))
|
requestDeleteBulkItemCategories: (ids) =>
|
||||||
|
dispatch(deleteBulkItemCategories({ ids })),
|
||||||
|
|
||||||
|
changeItemCategoriesView: (id) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.ITEM_CATEGORIES_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
addItemCategoriesTableQueries: (queries) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.ITEM_CATEGORIES_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps);
|
export default connect(null, mapDispatchToProps);
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import * as Yup from 'yup';
|
|||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick, omit } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
|
||||||
import BillFormHeader from './BillFormHeader';
|
import BillFormHeader from './BillFormHeader';
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
@@ -82,6 +83,7 @@ function BillForm({
|
|||||||
}
|
}
|
||||||
}, [changePageTitle, bill, formatMessage]);
|
}, [changePageTitle, bill, formatMessage]);
|
||||||
|
|
||||||
|
// @todo abstruct validation schema to sperated file.
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
vendor_id: Yup.number()
|
vendor_id: Yup.number()
|
||||||
.required()
|
.required()
|
||||||
@@ -102,7 +104,6 @@ function BillForm({
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(1024)
|
.max(1024)
|
||||||
.label(formatMessage({ id: 'note' })),
|
.label(formatMessage({ id: 'note' })),
|
||||||
|
|
||||||
entries: Yup.array().of(
|
entries: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
quantity: Yup.number().nullable(),
|
quantity: Yup.number().nullable(),
|
||||||
@@ -180,14 +181,6 @@ function BillForm({
|
|||||||
[bill, defaultInitialValues, defaultBill],
|
[bill, defaultInitialValues, defaultBill],
|
||||||
);
|
);
|
||||||
|
|
||||||
// const initialValues = useMemo(
|
|
||||||
// () => ({
|
|
||||||
// ...defaultInitialValues,
|
|
||||||
// entries: orderingIndex(defaultInitialValues.entries),
|
|
||||||
// }),
|
|
||||||
// [defaultInitialValues],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return bill && bill.media
|
return bill && bill.media
|
||||||
? bill.media.map((attach) => ({
|
? bill.media.map((attach) => ({
|
||||||
@@ -207,14 +200,10 @@ function BillForm({
|
|||||||
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
|
||||||
const entries = values.entries.filter(
|
|
||||||
(item) => item.item_id && item.quantity,
|
|
||||||
);
|
|
||||||
const form = {
|
const form = {
|
||||||
...values,
|
...values,
|
||||||
entries,
|
entries: values.entries.filter((item) => item.item_id && item.quantity),
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestForm = { ...form };
|
const requestForm = { ...form };
|
||||||
if (bill && bill.id) {
|
if (bill && bill.id) {
|
||||||
requestEditBill(bill.id, requestForm)
|
requestEditBill(bill.id, requestForm)
|
||||||
@@ -243,8 +232,8 @@ function BillForm({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
resetForm();
|
|
||||||
saveBillSubmit({ action: 'new', ...payload });
|
saveBillSubmit({ action: 'new', ...payload });
|
||||||
|
resetForm();
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -292,10 +281,6 @@ function BillForm({
|
|||||||
orderingIndex([...formik.values.entries, defaultBill]),
|
orderingIndex([...formik.values.entries, defaultBill]),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(formik.errors, 'Errors');
|
|
||||||
console.log(formik.values, 'values');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'bill-form'}>
|
<div className={'bill-form'}>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@@ -306,15 +291,25 @@ function BillForm({
|
|||||||
onClickAddNewRow={onClickAddNewRow}
|
onClickAddNewRow={onClickAddNewRow}
|
||||||
onClickClearAllLines={onClickCleanAllLines}
|
onClickClearAllLines={onClickCleanAllLines}
|
||||||
/>
|
/>
|
||||||
<FormGroup label={<T id={'note'} />} className={'form-group--'}>
|
<Row>
|
||||||
<TextArea growVertically={true} {...formik.getFieldProps('note')} />
|
<Col>
|
||||||
</FormGroup>
|
<FormGroup label={<T id={'note'} />} className={'form-group--'}>
|
||||||
<Dragzone
|
<TextArea
|
||||||
initialFiles={initialAttachmentFiles}
|
growVertically={true}
|
||||||
onDrop={handleDropFiles}
|
{...formik.getFieldProps('note')}
|
||||||
onDeleteFile={handleDeleteFile}
|
/>
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
</FormGroup>
|
||||||
/>
|
</Col>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</form>
|
</form>
|
||||||
<BillFormFooter
|
<BillFormFooter
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default function BillFormFooter({
|
|||||||
bill,
|
bill,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={'form__floating-footer'}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
|
|||||||
@@ -76,12 +76,10 @@ function BillFormHeader({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(vendorsCurrentPage, 'vendorsCurrentPage');
|
|
||||||
console.log(vendorItems, 'vendorItems');
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="page-form page-form--bill">
|
||||||
<div>
|
<div className={'page-form__primary-section'}>
|
||||||
{/* vendor account name */}
|
{/* Vendor account name */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'vendor_name'} />}
|
label={<T id={'vendor_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ function BillsDataTable({
|
|||||||
text={formatMessage({ id: 'delete_bill' })}
|
text={formatMessage({ id: 'delete_bill' })}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={handleDeleteBill(bill)}
|
onClick={handleDeleteBill(bill)}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,20 +3,25 @@ import { getResourceViews } from 'store/customViews/customViews.selectors';
|
|||||||
import {
|
import {
|
||||||
getBillCurrentPageFactory,
|
getBillCurrentPageFactory,
|
||||||
getBillPaginationMetaFactory,
|
getBillPaginationMetaFactory,
|
||||||
getBillTableQuery,
|
getBillTableQueryFactory,
|
||||||
} from 'store/Bills/bills.selectors';
|
} from 'store/Bills/bills.selectors';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getBillsItems = getBillCurrentPageFactory();
|
const getBillsItems = getBillCurrentPageFactory();
|
||||||
const getBillsPaginationMeta = getBillPaginationMetaFactory();
|
const getBillsPaginationMeta = getBillPaginationMetaFactory();
|
||||||
|
const getBillTableQuery = getBillTableQueryFactory();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const query = getBillTableQuery(state, props);
|
const tableQuery = getBillTableQuery(state, props);
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
billsCurrentPage: getBillsItems(state, props, query),
|
billsCurrentPage: getBillsItems(state, props, tableQuery),
|
||||||
billsViews: getResourceViews(state, props, 'bills'),
|
billsViews: getResourceViews(state, props, 'bills'),
|
||||||
billsItems: state.bills.items,
|
billsItems: state.bills.items,
|
||||||
billsTableQuery: query,
|
billsTableQuery: tableQuery,
|
||||||
billsPageination: getBillsPaginationMeta(state, props, query),
|
|
||||||
|
// @todo un-unncessery shit.
|
||||||
|
billsPageination: getBillsPaginationMeta(state, props, tableQuery),
|
||||||
billsLoading: state.bills.loading,
|
billsLoading: state.bills.loading,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
import React, {
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
import * as Yup from 'yup';
|
|
||||||
import { useFormik } from 'formik';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
|
||||||
|
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
|
||||||
import { pick, omit } from 'lodash';
|
|
||||||
|
|
||||||
import BillFormHeader from './BillFormHeader';
|
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
|
||||||
import BillFormFooter from './BillFormFooter';
|
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|
||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
|
||||||
import withBillActions from './withBillActions';
|
|
||||||
|
|
||||||
import { AppToaster } from 'components';
|
|
||||||
import Dragzone from 'components/Dragzone';
|
|
||||||
import useMedia from 'hooks/useMedia';
|
|
||||||
|
|
||||||
import { compose, repeatValue } from 'utils';
|
|
||||||
|
|
||||||
const MIN_LINES_NUMBER = 4;
|
|
||||||
|
|
||||||
function BillForm({
|
|
||||||
//#WithMedia
|
|
||||||
requestSubmitMedia,
|
|
||||||
requestDeleteMedia,
|
|
||||||
|
|
||||||
//#withBillActions
|
|
||||||
requestSubmitBill,
|
|
||||||
|
|
||||||
//#withDashboard
|
|
||||||
changePageTitle,
|
|
||||||
changePageSubtitle,
|
|
||||||
|
|
||||||
//#withBillDetail
|
|
||||||
bill,
|
|
||||||
|
|
||||||
//#Own Props
|
|
||||||
onFormSubmit,
|
|
||||||
onCancelForm,
|
|
||||||
}) {
|
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
const [payload, setPayload] = useState({});
|
|
||||||
|
|
||||||
const {
|
|
||||||
setFiles,
|
|
||||||
saveMedia,
|
|
||||||
deletedFiles,
|
|
||||||
setDeletedFiles,
|
|
||||||
deleteMedia,
|
|
||||||
} = useMedia({
|
|
||||||
saveCallback: requestSubmitMedia,
|
|
||||||
deleteCallback: requestDeleteMedia,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleDropFiles = useCallback((_files) => {
|
|
||||||
setFiles(_files.filter((file) => file.uploaded === false));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const savedMediaIds = useRef([]);
|
|
||||||
const clearSavedMediaIds = () => {
|
|
||||||
savedMediaIds.current = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (bill && bill.id) {
|
|
||||||
changePageTitle(formatMessage({ id: 'edit_bill' }));
|
|
||||||
} else {
|
|
||||||
changePageTitle(formatMessage({ id: 'new_bill' }));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
|
||||||
vendor_id: Yup.number()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'vendor_name_' })),
|
|
||||||
bill_date: Yup.date()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'bill_date_' })),
|
|
||||||
due_date: Yup.date()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'due_date_' })),
|
|
||||||
bill_number: Yup.number()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'bill_number_' })),
|
|
||||||
reference_no: Yup.string().min(1).max(255),
|
|
||||||
status: Yup.string().required(),
|
|
||||||
note: Yup.string()
|
|
||||||
.trim()
|
|
||||||
.min(1)
|
|
||||||
.max(1024)
|
|
||||||
.label(formatMessage({ id: 'note' })),
|
|
||||||
|
|
||||||
entries: Yup.array().of(
|
|
||||||
Yup.object().shape({
|
|
||||||
quantity: Yup.number().nullable(),
|
|
||||||
rate: Yup.number().nullable(),
|
|
||||||
item_id: Yup.number()
|
|
||||||
.nullable()
|
|
||||||
.when(['quantity', 'rate'], {
|
|
||||||
is: (quantity, rate) => quantity || rate,
|
|
||||||
then: Yup.number().required(),
|
|
||||||
}),
|
|
||||||
total: Yup.number().nullable(),
|
|
||||||
discount: Yup.number().nullable(),
|
|
||||||
description: Yup.string().nullable(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveBillSubmit = useCallback(
|
|
||||||
(payload) => {
|
|
||||||
onFormSubmit && onFormSubmit(payload);
|
|
||||||
},
|
|
||||||
[onFormSubmit],
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultBill = useMemo(() => ({
|
|
||||||
index: 0,
|
|
||||||
item_id: null,
|
|
||||||
rate: null,
|
|
||||||
discount: null,
|
|
||||||
quantity: null,
|
|
||||||
description: '',
|
|
||||||
status: '',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const defaultInitialValues = useMemo(
|
|
||||||
() => ({
|
|
||||||
accept: '',
|
|
||||||
vendor_name: '',
|
|
||||||
bill_number: '',
|
|
||||||
bill_date: moment(new Date()).format('YYYY-MM-DD'),
|
|
||||||
due_date: moment(new Date()).format('YYYY-MM-DD'),
|
|
||||||
reference_no: '',
|
|
||||||
note: '',
|
|
||||||
entries: [...repeatValue(defaultBill, MIN_LINES_NUMBER)],
|
|
||||||
}),
|
|
||||||
[defaultBill],
|
|
||||||
);
|
|
||||||
|
|
||||||
const orderingIndex = (_invoice) => {
|
|
||||||
return _invoice.map((item, index) => ({
|
|
||||||
...item,
|
|
||||||
index: index + 1,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialValues = useMemo(
|
|
||||||
() => ({
|
|
||||||
...defaultInitialValues,
|
|
||||||
entries: orderingIndex(defaultInitialValues.entries),
|
|
||||||
}),
|
|
||||||
[defaultInitialValues],
|
|
||||||
);
|
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
|
||||||
return bill && bill.media
|
|
||||||
? bill.media.map((attach) => ({
|
|
||||||
preview: attach.attachment_file,
|
|
||||||
uploaded: true,
|
|
||||||
metadata: { ...attach },
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
}, [bill]);
|
|
||||||
|
|
||||||
const formik = useFormik({
|
|
||||||
enableReinitialize: true,
|
|
||||||
validationSchema,
|
|
||||||
initialValues: {
|
|
||||||
...initialValues,
|
|
||||||
},
|
|
||||||
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
|
||||||
setSubmitting(true);
|
|
||||||
const entries = values.entries.map((item) => omit(item, ['total']));
|
|
||||||
|
|
||||||
const form = {
|
|
||||||
...values,
|
|
||||||
entries,
|
|
||||||
};
|
|
||||||
const saveBill = (mediaIds) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const requestForm = { ...form, media_ids: mediaIds };
|
|
||||||
|
|
||||||
requestSubmitBill(requestForm)
|
|
||||||
.then((response) => {
|
|
||||||
AppToaster.show({
|
|
||||||
message: formatMessage(
|
|
||||||
{ id: 'the_bill_has_been_successfully_created' },
|
|
||||||
{ number: values.bill_number },
|
|
||||||
),
|
|
||||||
intent: Intent.SUCCESS,
|
|
||||||
});
|
|
||||||
setSubmitting(false);
|
|
||||||
resetForm();
|
|
||||||
saveBillSubmit({ action: 'new', ...payload });
|
|
||||||
clearSavedMediaIds();
|
|
||||||
})
|
|
||||||
.catch((errors) => {
|
|
||||||
setSubmitting(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.all([saveMedia(), deleteMedia()])
|
|
||||||
.then(([savedMediaResponses]) => {
|
|
||||||
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
|
||||||
savedMediaIds.current = mediaIds;
|
|
||||||
return savedMediaResponses;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return saveBill(savedMediaIds.current);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleSubmitClick = useCallback(
|
|
||||||
(payload) => {
|
|
||||||
setPayload(payload);
|
|
||||||
formik.submitForm();
|
|
||||||
},
|
|
||||||
[setPayload, formik],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCancelClick = useCallback(
|
|
||||||
(payload) => {
|
|
||||||
onCancelForm && onCancelForm(payload);
|
|
||||||
},
|
|
||||||
[onCancelForm],
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(formik.errors, 'Bill');
|
|
||||||
const handleDeleteFile = useCallback(
|
|
||||||
(_deletedFiles) => {
|
|
||||||
_deletedFiles.forEach((deletedFile) => {
|
|
||||||
if (deletedFile.uploaded && deletedFile.metadata.id) {
|
|
||||||
setDeletedFiles([...deletedFiles, deletedFile.metadata.id]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[setDeletedFiles, deletedFiles],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onClickCleanAllLines = () => {
|
|
||||||
formik.setFieldValue(
|
|
||||||
'entries',
|
|
||||||
orderingIndex([...repeatValue(defaultBill, MIN_LINES_NUMBER)]),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickAddNewRow = () => {
|
|
||||||
formik.setFieldValue(
|
|
||||||
'entries',
|
|
||||||
orderingIndex([...formik.values.entries, defaultBill]),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'bill-form'}>
|
|
||||||
<form onSubmit={formik.handleSubmit}>
|
|
||||||
<BillFormHeader formik={formik} />
|
|
||||||
<EstimatesItemsTable
|
|
||||||
formik={formik}
|
|
||||||
entries={formik.values.entries}
|
|
||||||
onClickAddNewRow={onClickAddNewRow}
|
|
||||||
onClickClearAllLines={onClickCleanAllLines}
|
|
||||||
/>
|
|
||||||
<FormGroup label={<T id={'note'} />} className={'form-group--'}>
|
|
||||||
<TextArea growVertically={true} {...formik.getFieldProps('note')} />
|
|
||||||
</FormGroup>
|
|
||||||
<Dragzone
|
|
||||||
initialFiles={initialAttachmentFiles}
|
|
||||||
onDrop={handleDropFiles}
|
|
||||||
onDeleteFile={handleDeleteFile}
|
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
|
||||||
/>
|
|
||||||
<BillFormFooter
|
|
||||||
formik={formik}
|
|
||||||
onSubmit={handleSubmitClick}
|
|
||||||
onCancelClick={handleCancelClick}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
withBillActions,
|
|
||||||
withDashboardActions,
|
|
||||||
withMediaActions,
|
|
||||||
)(BillForm);
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
import React, { useMemo, useCallback, useState } from 'react';
|
|
||||||
import {
|
|
||||||
FormGroup,
|
|
||||||
InputGroup,
|
|
||||||
Intent,
|
|
||||||
Position,
|
|
||||||
MenuItem,
|
|
||||||
Classes,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
|
||||||
import { Row, Col } from 'react-grid-system';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import {
|
|
||||||
AccountsSelectList,
|
|
||||||
ListSelect,
|
|
||||||
ErrorMessage,
|
|
||||||
FieldRequiredHint,
|
|
||||||
Hint,
|
|
||||||
} from 'components';
|
|
||||||
|
|
||||||
import withCustomers from 'containers/Customers/withCustomers';
|
|
||||||
import withAccounts from 'containers/Accounts/withAccounts';
|
|
||||||
|
|
||||||
function BillFormHeader({
|
|
||||||
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
|
||||||
|
|
||||||
//#withCustomers
|
|
||||||
customers,
|
|
||||||
//#withAccouts
|
|
||||||
accountsList,
|
|
||||||
}) {
|
|
||||||
const handleDateChange = useCallback(
|
|
||||||
(date_filed) => (date) => {
|
|
||||||
const formatted = moment(date).format('YYYY-MM-DD');
|
|
||||||
setFieldValue(date_filed, formatted);
|
|
||||||
},
|
|
||||||
[setFieldValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onChangeSelected = useCallback(
|
|
||||||
(filedName) => {
|
|
||||||
return (item) => {
|
|
||||||
setFieldValue(filedName, item.id);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
[setFieldValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const vendorNameRenderer = useCallback(
|
|
||||||
(accept, { handleClick }) => (
|
|
||||||
<MenuItem
|
|
||||||
key={accept.id}
|
|
||||||
text={accept.display_name}
|
|
||||||
onClick={handleClick}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter vendor name
|
|
||||||
const filterVendorAccount = (query, vendor, _index, exactMatch) => {
|
|
||||||
const normalizedTitle = vendor.display_name.toLowerCase();
|
|
||||||
const normalizedQuery = query.toLowerCase();
|
|
||||||
if (exactMatch) {
|
|
||||||
return normalizedTitle === normalizedQuery;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
`${vendor.display_name} ${normalizedTitle}`.indexOf(normalizedQuery) >=
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{/* vendor account name */}
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'vendor_name'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
|
||||||
intent={errors.vendor_id && touched.vendor_id && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name={'vendor_id'} {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ListSelect
|
|
||||||
items={customers}
|
|
||||||
noResults={<MenuItem disabled={true} text="No results." />}
|
|
||||||
itemRenderer={vendorNameRenderer}
|
|
||||||
itemPredicate={filterVendorAccount}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
onItemSelect={onChangeSelected('vendor_id')}
|
|
||||||
selectedItem={values.vendor_id}
|
|
||||||
selectedItemProp={'id'}
|
|
||||||
defaultText={<T id={'select_vendor_account'} />}
|
|
||||||
labelProp={'display_name'}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'bill_date'} />}
|
|
||||||
inline={true}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
intent={errors.bill_date && touched.bill_date && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name="bill_date" {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
|
||||||
value={tansformDateValue(values.bill_date)}
|
|
||||||
onChange={handleDateChange('bill_date')}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'due_date'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
intent={errors.due_date && touched.due_date && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name="due_date" {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
|
||||||
value={tansformDateValue(values.due_date)}
|
|
||||||
onChange={handleDateChange('due_date')}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/* bill number */}
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'bill_number'} />}
|
|
||||||
inline={true}
|
|
||||||
className={('form-group--estimate', Classes.FILL)}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
|
||||||
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name="bill_number" {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
|
|
||||||
minimal={true}
|
|
||||||
{...getFieldProps('bill_number')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'reference'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--reference', Classes.FILL)}
|
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
|
||||||
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
|
||||||
minimal={true}
|
|
||||||
{...getFieldProps('reference_no')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
withCustomers(({ customers }) => ({
|
|
||||||
customers,
|
|
||||||
})),
|
|
||||||
withAccounts(({ accountsList }) => ({
|
|
||||||
accountsList,
|
|
||||||
})),
|
|
||||||
)(BillFormHeader);
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
|
||||||
import { useQuery } from 'react-query';
|
|
||||||
|
|
||||||
import BillForm from './BillForm';
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
|
|
||||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
|
||||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
|
||||||
|
|
||||||
import { compose } from 'utils';
|
|
||||||
|
|
||||||
function Bills({
|
|
||||||
//#withwithAccountsActions
|
|
||||||
requestFetchAccounts,
|
|
||||||
|
|
||||||
//#withCustomersActions
|
|
||||||
requestFetchCustomers,
|
|
||||||
|
|
||||||
//#withItemsActions
|
|
||||||
requestFetchItems,
|
|
||||||
}) {
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
// Handle fetch accounts
|
|
||||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
|
||||||
requestFetchAccounts(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle fetch customers data table
|
|
||||||
const fetchCustomers = useQuery('customers-table', () =>
|
|
||||||
requestFetchCustomers({}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle fetch Items data table or list
|
|
||||||
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
|
||||||
|
|
||||||
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
|
||||||
history.goBack();
|
|
||||||
}, [history]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider
|
|
||||||
loading={
|
|
||||||
fetchCustomers.isFetching ||
|
|
||||||
fetchItems.isFetching ||
|
|
||||||
fetchAccounts.isFetching
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<BillForm onSubmit={handleFormSubmit} onCancel={handleCancel} />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
withCustomersActions,
|
|
||||||
withItemsActions,
|
|
||||||
withAccountsActions,
|
|
||||||
)(Bills);
|
|
||||||
93
client/src/containers/Purchases/PaymentMades/PaymentMade.js
Normal file
93
client/src/containers/Purchases/PaymentMades/PaymentMade.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import React, { useCallback, useState, useEffect, useMemo } from 'react';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import PaymentMadeForm from './PaymentMadeForm';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import withVenderActions from 'containers/Vendors/withVendorActions';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
import withBillActions from '../Bill/withBillActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentMade({
|
||||||
|
//#withwithAccountsActions
|
||||||
|
requestFetchAccounts,
|
||||||
|
|
||||||
|
//#withVenderActions
|
||||||
|
requestFetchVendorsTable,
|
||||||
|
|
||||||
|
//#withItemsActions
|
||||||
|
requestFetchItems,
|
||||||
|
|
||||||
|
//#withPaymentMadesActions
|
||||||
|
requestFetchPaymentMade,
|
||||||
|
|
||||||
|
//#withBillActions
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { id } = useParams();
|
||||||
|
const [venderId, setVenderId] = useState(null);
|
||||||
|
|
||||||
|
// Handle fetch accounts data
|
||||||
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle fetch Items data table or list
|
||||||
|
const fetchItems = useQuery('items-list', () => requestFetchItems({}));
|
||||||
|
|
||||||
|
// Handle fetch venders data table or list
|
||||||
|
const fetchVenders = useQuery('venders-list', () =>
|
||||||
|
requestFetchVendorsTable({}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchPaymentMade = useQuery(
|
||||||
|
['payment-made', id],
|
||||||
|
(key, _id) => requestFetchPaymentMade(_id),
|
||||||
|
{ enabled: !!id },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFormSubmit = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
payload.redirect && history.push('/payment-mades');
|
||||||
|
},
|
||||||
|
[history],
|
||||||
|
);
|
||||||
|
const handleCancel = useCallback(() => {
|
||||||
|
history.goBack();
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
const handleVenderChange = (venderId) => {
|
||||||
|
setVenderId(venderId);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={
|
||||||
|
fetchVenders.isFetching ||
|
||||||
|
fetchItems.isFetching ||
|
||||||
|
fetchAccounts.isFetching ||
|
||||||
|
fetchPaymentMade.isFetching
|
||||||
|
}
|
||||||
|
name={'payment-made'}
|
||||||
|
>
|
||||||
|
<PaymentMadeForm
|
||||||
|
onFormSubmit={handleFormSubmit}
|
||||||
|
paymentMadeId={id}
|
||||||
|
onCancelForm={handleCancel}
|
||||||
|
onVenderChange={handleVenderChange}
|
||||||
|
/>
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withVenderActions,
|
||||||
|
withItemsActions,
|
||||||
|
withAccountsActions,
|
||||||
|
withBillActions,
|
||||||
|
withPaymentMadeActions,
|
||||||
|
)(PaymentMade);
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import React, { useCallback, useState, useMemo } from 'react';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Popover,
|
||||||
|
NavbarDivider,
|
||||||
|
NavbarGroup,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
Position,
|
||||||
|
Intent,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
|
|
||||||
|
import { If, DashboardActionViewsList } from 'components';
|
||||||
|
|
||||||
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
import withPaymentMade from './withPaymentMade';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentMadeActionsBar({
|
||||||
|
//#withResourceDetail
|
||||||
|
resourceFields,
|
||||||
|
|
||||||
|
//#withPaymentMades
|
||||||
|
paymentMadeViews,
|
||||||
|
|
||||||
|
//#withPaymentMadesActions
|
||||||
|
addPaymentMadesTableQueries,
|
||||||
|
|
||||||
|
// #own Porps
|
||||||
|
onFilterChanged,
|
||||||
|
selectedRows = [],
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { path } = useRouteMatch();
|
||||||
|
const [filterCount, setFilterCount] = useState(0);
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const handleClickNewPaymentMade = useCallback(() => {
|
||||||
|
history.push('/payment-made/new');
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
// const filterDropdown = FilterDropdown({
|
||||||
|
// initialCondition: {
|
||||||
|
// fieldKey: '',
|
||||||
|
// compatator: 'contains',
|
||||||
|
// value: '',
|
||||||
|
// },
|
||||||
|
// fields: resourceFields,
|
||||||
|
// onFilterChange: (filterConditions) => {
|
||||||
|
// addPaymentMadesTableQueries({
|
||||||
|
// filter_roles: filterConditions || '',
|
||||||
|
// });
|
||||||
|
// onFilterChanged && onFilterChanged(filterConditions);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||||
|
selectedRows,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardActionsBar>
|
||||||
|
<NavbarGroup>
|
||||||
|
<DashboardActionViewsList
|
||||||
|
resourceName={'bill_payments'}
|
||||||
|
views={paymentMadeViews}
|
||||||
|
/>
|
||||||
|
<NavbarDivider />
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'plus'} />}
|
||||||
|
text={<T id={'new_payment_made'} />}
|
||||||
|
onClick={handleClickNewPaymentMade}
|
||||||
|
/>
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
// content={filterDropdown}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.BOTTOM_LEFT}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={classNames(Classes.MINIMAL)}
|
||||||
|
text={
|
||||||
|
filterCount <= 0 ? (
|
||||||
|
<T id={'filter'} />
|
||||||
|
) : (
|
||||||
|
`${filterCount} ${formatMessage({ id: 'filters_applied' })}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
icon={<Icon icon={'filter-16'} iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
<If condition={hasSelectedRows}>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||||
|
text={<T id={'delete'} />}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
// onClick={handleBulkDelete}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'print-16'} iconSize={'16'} />}
|
||||||
|
text={<T id={'print'} />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'file-import-16'} />}
|
||||||
|
text={<T id={'import'} />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'file-export-16'} iconSize={'16'} />}
|
||||||
|
text={<T id={'export'} />}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</DashboardActionsBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
resourceName: 'bill_payments',
|
||||||
|
});
|
||||||
|
|
||||||
|
const withPaymentMadeActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withPaymentMadeActionsBar,
|
||||||
|
withResourceDetail(({ resourceFields }) => ({
|
||||||
|
resourceFields,
|
||||||
|
})),
|
||||||
|
withPaymentMade(({ paymentMadeViews }) => ({
|
||||||
|
paymentMadeViews,
|
||||||
|
})),
|
||||||
|
withPaymentMadeActions,
|
||||||
|
)(PaymentMadeActionsBar);
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Intent,
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Popover,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
MenuDivider,
|
||||||
|
Position,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { withRouter } from 'react-router';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
|
import { DataTable, Money, Icon } from 'components';
|
||||||
|
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withViewDetails from 'containers/Views/withViewDetails';
|
||||||
|
|
||||||
|
import withPaymentMade from './withPaymentMade';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
import withCurrentView from 'containers/Views/withCurrentView';
|
||||||
|
|
||||||
|
function PaymentMadeDataTable({
|
||||||
|
//#withPaymentMades
|
||||||
|
paymentMadeCurrentPage,
|
||||||
|
paymentMadePageination,
|
||||||
|
paymentMadeLoading,
|
||||||
|
paymentMadeItems,
|
||||||
|
|
||||||
|
// #withDashboardActions
|
||||||
|
changeCurrentView,
|
||||||
|
changePageSubtitle,
|
||||||
|
setTopbarEditView,
|
||||||
|
|
||||||
|
// #withView
|
||||||
|
viewMeta,
|
||||||
|
|
||||||
|
//#OwnProps
|
||||||
|
loading,
|
||||||
|
onFetchData,
|
||||||
|
onEditPaymentMade,
|
||||||
|
onDeletePaymentMade,
|
||||||
|
onSelectedRowsChange,
|
||||||
|
}) {
|
||||||
|
const [initialMount, setInitialMount] = useState(false);
|
||||||
|
const { custom_view_id: customViewId } = useParams();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialMount(false);
|
||||||
|
}, [customViewId]);
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
if (!paymentMadeLoading) {
|
||||||
|
setInitialMount(true);
|
||||||
|
}
|
||||||
|
}, [paymentMadeLoading, setInitialMount]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (customViewId) {
|
||||||
|
// changeCurrentView(customViewId);
|
||||||
|
// setTopbarEditView(customViewId);
|
||||||
|
// }
|
||||||
|
// changePageSubtitle(customViewId && viewMeta ? viewMeta.name : '');
|
||||||
|
// }, [
|
||||||
|
// customViewId,
|
||||||
|
// changeCurrentView,
|
||||||
|
// changePageSubtitle,
|
||||||
|
// setTopbarEditView,
|
||||||
|
// viewMeta,
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
const handleEditPaymentMade = useCallback(
|
||||||
|
(paymentMade) => () => {
|
||||||
|
onEditPaymentMade && onEditPaymentMade(paymentMade);
|
||||||
|
},
|
||||||
|
[onEditPaymentMade],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDeletePaymentMade = useCallback(
|
||||||
|
(paymentMade) => () => {
|
||||||
|
onDeletePaymentMade && onDeletePaymentMade(paymentMade);
|
||||||
|
},
|
||||||
|
[onDeletePaymentMade],
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionMenuList = useCallback(
|
||||||
|
(paymentMade) => (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem text={formatMessage({ id: 'view_details' })} />
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem
|
||||||
|
text={formatMessage({ id: 'edit_payment_made' })}
|
||||||
|
onClick={handleEditPaymentMade(paymentMade)}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
text={formatMessage({ id: 'delete_payment_made' })}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={handleDeletePaymentMade(paymentMade)}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
),
|
||||||
|
[handleDeletePaymentMade, handleEditPaymentMade, formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onRowContextMenu = useCallback(
|
||||||
|
(cell) => {
|
||||||
|
return actionMenuList(cell.row.original);
|
||||||
|
},
|
||||||
|
[actionMenuList],
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'payment_date',
|
||||||
|
Header: formatMessage({ id: 'payment_date' }),
|
||||||
|
accessor: (r) => moment(r.payment_date).format('YYYY MMM DD'),
|
||||||
|
width: 140,
|
||||||
|
className: 'payment_date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'vendor_id',
|
||||||
|
Header: formatMessage({ id: 'vendor_name' }),
|
||||||
|
accessor: 'vendor.display_name',
|
||||||
|
width: 140,
|
||||||
|
className: 'vendor_id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'payment_number',
|
||||||
|
Header: formatMessage({ id: 'payment_number' }),
|
||||||
|
accessor: (row) => `#${row.payment_number}`,
|
||||||
|
width: 140,
|
||||||
|
className: 'payment_number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'payment_account_id',
|
||||||
|
Header: formatMessage({ id: 'payment_account' }),
|
||||||
|
accessor: 'payment_account.name',
|
||||||
|
width: 140,
|
||||||
|
className: 'payment_account_id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'amount',
|
||||||
|
Header: formatMessage({ id: 'amount' }),
|
||||||
|
accessor: (r) => <Money amount={r.amount} currency={'USD'} />,
|
||||||
|
width: 140,
|
||||||
|
className: 'amount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'reference',
|
||||||
|
Header: formatMessage({ id: 'reference' }),
|
||||||
|
accessor: 'reference',
|
||||||
|
width: 140,
|
||||||
|
className: 'reference',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
Header: '',
|
||||||
|
Cell: ({ cell }) => (
|
||||||
|
<Popover
|
||||||
|
content={actionMenuList(cell.row.original)}
|
||||||
|
position={Position.RIGHT_BOTTOM}
|
||||||
|
>
|
||||||
|
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||||
|
</Popover>
|
||||||
|
),
|
||||||
|
className: 'actions',
|
||||||
|
width: 50,
|
||||||
|
disableResizing: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[actionMenuList, formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDataTableFetchData = useCallback(
|
||||||
|
(...args) => {
|
||||||
|
onFetchData && onFetchData(...args);
|
||||||
|
},
|
||||||
|
[onFetchData],
|
||||||
|
);
|
||||||
|
const handleSelectedRowsChange = useCallback(
|
||||||
|
(selectedRows) => {
|
||||||
|
onSelectedRowsChange &&
|
||||||
|
onSelectedRowsChange(selectedRows.map((s) => s.original));
|
||||||
|
},
|
||||||
|
[onSelectedRowsChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={paymentMadeCurrentPage}
|
||||||
|
onFetchData={handleDataTableFetchData}
|
||||||
|
manualSortBy={true}
|
||||||
|
selectionColumn={true}
|
||||||
|
noInitialFetch={true}
|
||||||
|
sticky={true}
|
||||||
|
loading={paymentMadeLoading && !initialMount}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
rowContextMenu={onRowContextMenu}
|
||||||
|
pagination={true}
|
||||||
|
pagesCount={paymentMadePageination.pagesCount}
|
||||||
|
initialPageSize={paymentMadePageination.pageSize}
|
||||||
|
initialPageIndex={paymentMadePageination.page - 1}
|
||||||
|
/>
|
||||||
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
withCurrentView,
|
||||||
|
withDialogActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withPaymentMadeActions,
|
||||||
|
withPaymentMade(
|
||||||
|
({
|
||||||
|
paymentMadeCurrentPage,
|
||||||
|
paymentMadeLoading,
|
||||||
|
paymentMadePageination,
|
||||||
|
}) => ({
|
||||||
|
paymentMadeCurrentPage,
|
||||||
|
paymentMadeLoading,
|
||||||
|
paymentMadePageination,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
withViewDetails(),
|
||||||
|
)(PaymentMadeDataTable);
|
||||||
320
client/src/containers/Purchases/PaymentMades/PaymentMadeForm.js
Normal file
320
client/src/containers/Purchases/PaymentMades/PaymentMadeForm.js
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
import React, {
|
||||||
|
useMemo,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import { pick, values } from 'lodash';
|
||||||
|
|
||||||
|
import PaymentMadeHeader from './PaymentMadeFormHeader';
|
||||||
|
import PaymentMadeItemsTable from './PaymentMadeItemsTable';
|
||||||
|
import PaymentMadeFooter from './PaymentMadeFormFooter';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
import withPaymentMadeDetail from './withPaymentMadeDetail';
|
||||||
|
import withPaymentMade from './withPaymentMade';
|
||||||
|
import { AppToaster } from 'components';
|
||||||
|
import Dragzone from 'components/Dragzone';
|
||||||
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
|
import { compose, repeatValue } from 'utils';
|
||||||
|
|
||||||
|
const MIN_LINES_NUMBER = 5;
|
||||||
|
|
||||||
|
function PaymentMadeForm({
|
||||||
|
//#withMedia
|
||||||
|
requestSubmitMedia,
|
||||||
|
requestDeleteMedia,
|
||||||
|
//#withPaymentMadesActions
|
||||||
|
|
||||||
|
requestSubmitPaymentMade,
|
||||||
|
requestEditPaymentMade,
|
||||||
|
|
||||||
|
//#withDashboard
|
||||||
|
changePageTitle,
|
||||||
|
changePageSubtitle,
|
||||||
|
|
||||||
|
//#withPaymentMadeDetail
|
||||||
|
paymentMade,
|
||||||
|
|
||||||
|
onFormSubmit,
|
||||||
|
onCancelForm,
|
||||||
|
onVenderChange,
|
||||||
|
}) {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [payload, setPayload] = useState({});
|
||||||
|
const { id } = useParams();
|
||||||
|
const {
|
||||||
|
setFiles,
|
||||||
|
saveMedia,
|
||||||
|
deletedFiles,
|
||||||
|
setDeletedFiles,
|
||||||
|
deleteMedia,
|
||||||
|
} = useMedia({
|
||||||
|
saveCallback: requestSubmitMedia,
|
||||||
|
deleteCallback: requestDeleteMedia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const savedMediaIds = useRef([]);
|
||||||
|
const clearSavedMediaIds = () => {
|
||||||
|
savedMediaIds.current = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onVenderChange && onVenderChange(formik.values.vendor_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (paymentMade && paymentMade.id) {
|
||||||
|
changePageTitle(formatMessage({ id: 'edit_payment_made' }));
|
||||||
|
} else {
|
||||||
|
changePageTitle(formatMessage({ id: 'payment_made' }));
|
||||||
|
}
|
||||||
|
}, [changePageTitle, paymentMade, formatMessage]);
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
vendor_id: Yup.string()
|
||||||
|
.label(formatMessage({ id: 'vendor_name_' }))
|
||||||
|
.required(),
|
||||||
|
payment_date: Yup.date()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'payment_date_' })),
|
||||||
|
payment_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'payment_account_' })),
|
||||||
|
payment_number: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'payment_no_' })),
|
||||||
|
reference: Yup.string().min(1).max(255).nullable(),
|
||||||
|
description: Yup.string(),
|
||||||
|
entries: Yup.array().of(
|
||||||
|
Yup.object().shape({
|
||||||
|
payment_amount: Yup.number().nullable(),
|
||||||
|
bill_number: Yup.number().nullable(),
|
||||||
|
amount: Yup.number().nullable(),
|
||||||
|
due_amount: Yup.number().nullable(),
|
||||||
|
bill_date: Yup.date(),
|
||||||
|
bill_id: Yup.number()
|
||||||
|
.nullable()
|
||||||
|
.when(['payment_amount'], {
|
||||||
|
is: (payment_amount) => payment_amount,
|
||||||
|
then: Yup.number().required(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const handleDropFiles = useCallback((_files) => {
|
||||||
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const savePaymentMadeSubmit = useCallback((payload) => {
|
||||||
|
onFormSubmit && onFormSubmit(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultPaymentMade = useMemo(
|
||||||
|
() => ({
|
||||||
|
bill_id: '',
|
||||||
|
bill_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
bill_number: '',
|
||||||
|
amount: '',
|
||||||
|
due_amount: '',
|
||||||
|
payment_amount: '',
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultInitialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
vendor_id: '',
|
||||||
|
payment_account_id: '',
|
||||||
|
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
reference: '',
|
||||||
|
payment_number: '',
|
||||||
|
// receive_amount: '',
|
||||||
|
description: '',
|
||||||
|
entries: [...repeatValue(defaultPaymentMade, MIN_LINES_NUMBER)],
|
||||||
|
}),
|
||||||
|
[defaultPaymentMade],
|
||||||
|
);
|
||||||
|
|
||||||
|
const orderingIndex = (_entries) => {
|
||||||
|
return _entries.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
index: index + 1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
...(paymentMade
|
||||||
|
? {
|
||||||
|
...pick(paymentMade, Object.keys(defaultInitialValues)),
|
||||||
|
entries: [
|
||||||
|
...paymentMade.entries.map((paymentMade) => ({
|
||||||
|
...pick(paymentMade, Object.keys(defaultPaymentMade)),
|
||||||
|
})),
|
||||||
|
...repeatValue(
|
||||||
|
defaultPaymentMade,
|
||||||
|
Math.max(MIN_LINES_NUMBER - paymentMade.entries.length, 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...defaultInitialValues,
|
||||||
|
entries: orderingIndex(defaultInitialValues.entries),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[paymentMade, defaultInitialValues, defaultPaymentMade],
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
|
return paymentMade && paymentMade.media
|
||||||
|
? paymentMade.media.map((attach) => ({
|
||||||
|
preview: attach.attachment_file,
|
||||||
|
uploaded: true,
|
||||||
|
metadata: { ...attach },
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
}, [paymentMade]);
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
validationSchema,
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
},
|
||||||
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
|
setSubmitting(true);
|
||||||
|
const entries = formik.values.entries.filter((item) => {
|
||||||
|
if (item.bill_id !== undefined) {
|
||||||
|
return { ...item };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const form = {
|
||||||
|
...values,
|
||||||
|
entries,
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestForm = { ...form };
|
||||||
|
|
||||||
|
if (paymentMade && paymentMade.id) {
|
||||||
|
requestEditPaymentMade(paymentMade.id, requestForm)
|
||||||
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_made_has_been_successfully_edited',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
savePaymentMadeSubmit({ action: 'update', ...payload });
|
||||||
|
resetForm();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
requestSubmitPaymentMade(requestForm)
|
||||||
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_made_has_been_successfully_created',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
resetForm();
|
||||||
|
savePaymentMadeSubmit({ action: 'new', ...payload });
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDeleteFile = useCallback(
|
||||||
|
(_deletedFiles) => {
|
||||||
|
_deletedFiles.forEach((deletedFile) => {
|
||||||
|
if (deletedFile.upload && deletedFile.metadata.id) {
|
||||||
|
setDeletedFiles([...deletedFiles, deletedFile.metadata.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setDeletedFiles, deletedFiles],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSubmitClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
setPayload(payload);
|
||||||
|
formik.submitForm();
|
||||||
|
},
|
||||||
|
[setPayload, formik],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancelClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onCancelForm && onCancelForm(payload);
|
||||||
|
},
|
||||||
|
[onCancelForm],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClickAddNewRow = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...formik.values.entries, defaultPaymentMade]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAllLines = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...repeatValue(defaultPaymentMade, MIN_LINES_NUMBER)]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
console.log(formik.errors, 'ERROR');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'payment_made_form'}>
|
||||||
|
<form onSubmit={formik.handleSubmit}>
|
||||||
|
<PaymentMadeHeader formik={formik} />
|
||||||
|
<PaymentMadeItemsTable
|
||||||
|
formik={formik}
|
||||||
|
entries={formik.values.entries}
|
||||||
|
// vendor_id={formik.values.vendor_id}
|
||||||
|
onClickAddNewRow={handleClickAddNewRow}
|
||||||
|
onClickClearAllLines={handleClearAllLines}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* <Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/> */}
|
||||||
|
</form>
|
||||||
|
<PaymentMadeFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
onCancel={handleCancelClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withPaymentMadeActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withMediaActions,
|
||||||
|
withPaymentMadeDetail(),
|
||||||
|
)(PaymentMadeForm);
|
||||||
@@ -2,14 +2,22 @@ import React from 'react';
|
|||||||
import { Intent, Button } from '@blueprintjs/core';
|
import { Intent, Button } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
export default function BillFormFooter({
|
export default function PaymentMadeFormFooter({
|
||||||
formik: { isSubmitting },
|
formik: { isSubmitting, resetForm },
|
||||||
onSubmitClick,
|
onSubmitClick,
|
||||||
onCancelClick,
|
onCancelClick,
|
||||||
|
paymentMade,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={'estimate-form__floating-footer'}>
|
||||||
<Button disabled={isSubmitting} intent={Intent.PRIMARY} type="submit">
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ redirect: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
<T id={'save_send'} />
|
<T id={'save_send'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -19,11 +27,14 @@ export default function BillFormFooter({
|
|||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
name={'save'}
|
name={'save'}
|
||||||
type="submit"
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ redirect: false });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<T id={'save'} />
|
<T id={'save'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button className={'ml1'} disabled={isSubmitting}>
|
<Button className={'ml1'} disabled={isSubmitting} s>
|
||||||
<T id={'clear'} />
|
<T id={'clear'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import React, { useMemo, useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
InputGroup,
|
||||||
|
Intent,
|
||||||
|
Position,
|
||||||
|
MenuItem,
|
||||||
|
Classes,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {
|
||||||
|
AccountsSelectList,
|
||||||
|
ListSelect,
|
||||||
|
ErrorMessage,
|
||||||
|
FieldRequiredHint,
|
||||||
|
} from 'components';
|
||||||
|
|
||||||
|
import withVender from 'containers/Vendors/withVendors';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
function PaymentMadeFormHeader({
|
||||||
|
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
||||||
|
|
||||||
|
//#withVender
|
||||||
|
vendorsCurrentPage,
|
||||||
|
//#withAccouts
|
||||||
|
accountsList,
|
||||||
|
}) {
|
||||||
|
const handleDateChange = useCallback(
|
||||||
|
(date_filed) => (date) => {
|
||||||
|
const formatted = moment(date).format('YYYY-MM-DD');
|
||||||
|
setFieldValue(date_filed, formatted);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleVenderRenderer = useCallback(
|
||||||
|
(vender, { handleClick }) => (
|
||||||
|
<MenuItem
|
||||||
|
key={vender.id}
|
||||||
|
text={vender.display_name}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFilterVender = (query, vender, index, exactMatch) => {
|
||||||
|
const normalizedTitle = vender.display_name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
`${vender.display_name} ${normalizedTitle}`.indexOf(normalizedQuery) >=
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeSelect = useCallback(
|
||||||
|
(filedName) => {
|
||||||
|
return (item) => {
|
||||||
|
setFieldValue(filedName, item.id);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
// Filter Payment accounts.
|
||||||
|
const paymentAccounts = useMemo(
|
||||||
|
() => accountsList.filter((a) => a?.type?.key === 'current_asset'),
|
||||||
|
[accountsList],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{/* Vend name */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'vendor_name'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={errors.vendor_id && touched.vendor_id && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name={'vendor_id'} {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListSelect
|
||||||
|
items={vendorsCurrentPage}
|
||||||
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
|
itemRenderer={handleVenderRenderer}
|
||||||
|
itemPredicate={handleFilterVender}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
onItemSelect={onChangeSelect('vendor_id')}
|
||||||
|
selectedItem={values.vendor_id}
|
||||||
|
selectedItemProp={'id'}
|
||||||
|
defaultText={<T id={'select_vender_account'} />}
|
||||||
|
labelProp={'display_name'}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* Payment date */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_date'} />}
|
||||||
|
inline={true}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
intent={errors.payment_date && touched.payment_date && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DateInput
|
||||||
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
|
value={tansformDateValue(values.payment_date)}
|
||||||
|
onChange={handleDateChange('payment_date')}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* payment number */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_no'} />}
|
||||||
|
inline={true}
|
||||||
|
className={('form-group--payment_number', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={
|
||||||
|
errors.payment_number && touched.payment_number && Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_number" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={
|
||||||
|
errors.payment_number && touched.payment_number && Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('payment_number')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* payment account */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_account'} />}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--payment_account_id',
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
|
inline={true}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={
|
||||||
|
errors.payment_account_id &&
|
||||||
|
touched.payment_account_id &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage
|
||||||
|
name={'payment_account_id'}
|
||||||
|
{...{ errors, touched }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AccountsSelectList
|
||||||
|
accounts={paymentAccounts}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
onAccountSelected={onChangeSelect('payment_account_id')}
|
||||||
|
defaultSelectText={<T id={'select_payment_account'} />}
|
||||||
|
selectedAccountId={values.payment_account_id}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* reference */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'reference'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--reference', Classes.FILL)}
|
||||||
|
intent={errors.reference && touched.reference && Intent.DANGER}
|
||||||
|
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={errors.reference && touched.reference && Intent.DANGER}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('reference')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withVender(({ vendorsCurrentPage }) => ({
|
||||||
|
vendorsCurrentPage,
|
||||||
|
})),
|
||||||
|
withAccounts(({ accountsList }) => ({
|
||||||
|
accountsList,
|
||||||
|
})),
|
||||||
|
)(PaymentMadeFormHeader);
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
|
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import { Icon, DataTable } from 'components';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { compose, formattedAmount, transformUpdatedRows } from 'utils';
|
||||||
|
import {
|
||||||
|
MoneyFieldCell,
|
||||||
|
DivFieldCell,
|
||||||
|
EmptyDiv,
|
||||||
|
} from 'components/DataTableCells';
|
||||||
|
|
||||||
|
import withBills from '../Bill/withBills';
|
||||||
|
import { omit, pick } from 'lodash';
|
||||||
|
|
||||||
|
const ActionsCellRenderer = ({
|
||||||
|
row: { index },
|
||||||
|
column: { id },
|
||||||
|
cell: { value },
|
||||||
|
data,
|
||||||
|
payload,
|
||||||
|
}) => {
|
||||||
|
if (data.length <= index + 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const onRemoveRole = () => {
|
||||||
|
payload.removeRow(index);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
|
||||||
|
<Button
|
||||||
|
icon={<Icon icon={'times-circle'} iconSize={14} />}
|
||||||
|
iconSize={14}
|
||||||
|
className="m12"
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={onRemoveRole}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CellRenderer = (content, type) => (props) => {
|
||||||
|
if (props.data.length === props.row.index + 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return content(props);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TotalCellRederer = (content, type) => (props) => {
|
||||||
|
if (props.data.length === props.row.index + 1) {
|
||||||
|
const total = props.data.reduce((total, entry) => {
|
||||||
|
const amount = parseInt(entry[type], 10);
|
||||||
|
const computed = amount ? total + amount : total;
|
||||||
|
|
||||||
|
return computed;
|
||||||
|
}, 0);
|
||||||
|
return <span>{formattedAmount(total, 'USD')}</span>;
|
||||||
|
}
|
||||||
|
return content(props);
|
||||||
|
};
|
||||||
|
|
||||||
|
function PaymentMadeItemsTable({
|
||||||
|
//#ownProps
|
||||||
|
onClickRemoveRow,
|
||||||
|
onClickAddNewRow,
|
||||||
|
onClickClearAllLines,
|
||||||
|
entries,
|
||||||
|
formik: { errors, setFieldValue, values },
|
||||||
|
}) {
|
||||||
|
const [rows, setRows] = useState([]);
|
||||||
|
const [entrie, setEntrie] = useState([]);
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRows([...entries.map((e) => ({ ...e }))]);
|
||||||
|
}, [entries]);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: '#',
|
||||||
|
accessor: 'index',
|
||||||
|
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
|
||||||
|
width: 40,
|
||||||
|
disableResizing: true,
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'Date' }),
|
||||||
|
id: 'bill_date',
|
||||||
|
accessor: (r) => moment(r.bill_date).format('YYYY MMM DD'),
|
||||||
|
Cell: CellRenderer(EmptyDiv, 'bill_date'),
|
||||||
|
disableSortBy: true,
|
||||||
|
disableResizing: true,
|
||||||
|
width: 250,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'bill_number' }),
|
||||||
|
accessor: (row) => `#${row.bill_number}`,
|
||||||
|
Cell: CellRenderer(EmptyDiv, 'bill_number'),
|
||||||
|
disableSortBy: true,
|
||||||
|
className: 'bill_number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'bill_amount' }),
|
||||||
|
accessor: 'amount',
|
||||||
|
Cell: CellRenderer(DivFieldCell, 'amount'),
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 100,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'amount_due' }),
|
||||||
|
accessor: 'due_amount',
|
||||||
|
Cell: TotalCellRederer(DivFieldCell, 'due_amount'),
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 150,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'payment_amount' }),
|
||||||
|
accessor: 'payment_amount',
|
||||||
|
Cell: TotalCellRederer(MoneyFieldCell, 'payment_amount'),
|
||||||
|
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 150,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: '',
|
||||||
|
accessor: 'action',
|
||||||
|
Cell: ActionsCellRenderer,
|
||||||
|
className: 'actions',
|
||||||
|
disableSortBy: true,
|
||||||
|
disableResizing: true,
|
||||||
|
width: 45,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemoveRow = useCallback(
|
||||||
|
(rowIndex) => {
|
||||||
|
if (rows.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const removeIndex = parseInt(rowIndex, 10);
|
||||||
|
const newRows = rows.filter((row, index) => index !== removeIndex);
|
||||||
|
|
||||||
|
setFieldValue(
|
||||||
|
'entries',
|
||||||
|
newRows.map((row, index) => ({
|
||||||
|
...omit(row),
|
||||||
|
index: index - 1,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
onClickRemoveRow && onClickRemoveRow(removeIndex);
|
||||||
|
},
|
||||||
|
[entrie, setFieldValue, onClickRemoveRow],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClickNewRow = () => {
|
||||||
|
onClickAddNewRow && onClickAddNewRow();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickClearAllLines = () => {
|
||||||
|
onClickClearAllLines && onClickClearAllLines();
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowClassNames = useCallback((row) => {
|
||||||
|
return { 'row--total': rows.length === row.index + 1 };
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleUpdateData = useCallback(
|
||||||
|
(rowIndex, columnId, value) => {
|
||||||
|
const newRows = rows.map((row, index) => {
|
||||||
|
if (index === rowIndex) {
|
||||||
|
return {
|
||||||
|
...rows[rowIndex],
|
||||||
|
[columnId]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
// setRows(newRows);
|
||||||
|
setFieldValue(
|
||||||
|
'entries',
|
||||||
|
newRows,
|
||||||
|
// .map((row) => ({
|
||||||
|
// ...pick(row, ['payment_amount']),
|
||||||
|
// invoice_id: row.id,
|
||||||
|
// })),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[rows, setFieldValue, setRows],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className={'estimate-form__table'}>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={rows}
|
||||||
|
rowClassNames={rowClassNames}
|
||||||
|
spinnerProps={false}
|
||||||
|
payload={{
|
||||||
|
errors: errors.entries || [],
|
||||||
|
updateData: handleUpdateData,
|
||||||
|
removeRow: handleRemoveRow,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={'mt1'}>
|
||||||
|
<Button
|
||||||
|
small={true}
|
||||||
|
className={'button--secondary button--new-line'}
|
||||||
|
onClick={onClickNewRow}
|
||||||
|
>
|
||||||
|
<T id={'new_lines'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
small={true}
|
||||||
|
className={'button--secondary button--clear-lines ml1'}
|
||||||
|
onClick={handleClickClearAllLines}
|
||||||
|
>
|
||||||
|
<T id={'clear_all_lines'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose()(PaymentMadeItemsTable);
|
||||||
|
// withBills(({}) => ({}))
|
||||||
184
client/src/containers/Purchases/PaymentMades/PaymentMadeList.js
Normal file
184
client/src/containers/Purchases/PaymentMades/PaymentMadeList.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import React, { useEffect, useCallback, useMemo, useState } from 'react';
|
||||||
|
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery, queryCache } from 'react-query';
|
||||||
|
import { Alert, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import PaymentMadeDataTable from './PaymentMadeDataTable';
|
||||||
|
import PaymentMadeActionsBar from './PaymentMadeActionsBar';
|
||||||
|
import PaymentMadeViewTabs from './PaymentMadeViewTabs';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
|
import withPaymentMades from './withPaymentMade';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentMadeList({
|
||||||
|
//#withDashboardActions
|
||||||
|
changePageTitle,
|
||||||
|
|
||||||
|
//#withViewsActions
|
||||||
|
requestFetchResourceViews,
|
||||||
|
requestFetchResourceFields,
|
||||||
|
|
||||||
|
//#withPaymentMades
|
||||||
|
paymentMadeTableQuery,
|
||||||
|
|
||||||
|
//#withPaymentMadesActions
|
||||||
|
requestFetchPaymentMadesTable,
|
||||||
|
requestDeletePaymentMade,
|
||||||
|
addPaymentMadesTableQueries,
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [deletePaymentMade, setDeletePaymentMade] = useState(false);
|
||||||
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changePageTitle(formatMessage({ id: 'payment_made_list' }));
|
||||||
|
}, [changePageTitle, formatMessage]);
|
||||||
|
|
||||||
|
const fetchResourceViews = useQuery(
|
||||||
|
['resource-views', 'bill_payments'],
|
||||||
|
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchResourceFields = useQuery(
|
||||||
|
['resource-fields', 'bill_payments'],
|
||||||
|
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchPaymentMades = useQuery(
|
||||||
|
['paymantMades-table', paymentMadeTableQuery],
|
||||||
|
() => requestFetchPaymentMadesTable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
//handle dalete Payment Made
|
||||||
|
const handleDeletePaymentMade = useCallback(
|
||||||
|
(paymentMade) => {
|
||||||
|
setDeletePaymentMade(paymentMade);
|
||||||
|
},
|
||||||
|
[setDeletePaymentMade],
|
||||||
|
);
|
||||||
|
|
||||||
|
// handle cancel Payment Made
|
||||||
|
const handleCancelPaymentMadeDelete = useCallback(() => {
|
||||||
|
setDeletePaymentMade(false);
|
||||||
|
}, [setDeletePaymentMade]);
|
||||||
|
|
||||||
|
// handleConfirm delete payment made
|
||||||
|
const handleConfirmPaymentMadeDelete = useCallback(() => {
|
||||||
|
requestDeletePaymentMade(deletePaymentMade.id).then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_made_has_been_successfully_deleted',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setDeletePaymentMade(false);
|
||||||
|
});
|
||||||
|
}, [deletePaymentMade, requestDeletePaymentMade, formatMessage]);
|
||||||
|
|
||||||
|
const handleEditPaymentMade = useCallback((payment) => {
|
||||||
|
history.push(`/payment-made/${payment.id}/edit`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculates the selected rows count.
|
||||||
|
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
|
||||||
|
selectedRows,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleFetchData = useCallback(
|
||||||
|
({ pageIndex, pageSize, sortBy }) => {
|
||||||
|
const page = pageIndex + 1;
|
||||||
|
|
||||||
|
addPaymentMadesTableQueries({
|
||||||
|
...(sortBy.length > 0
|
||||||
|
? {
|
||||||
|
column_sort_by: sortBy[0].id,
|
||||||
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
page_size: pageSize,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addPaymentMadesTableQueries],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle filter change to re-fetch data-table.
|
||||||
|
const handleFilterChanged = useCallback(() => {}, [fetchPaymentMades]);
|
||||||
|
|
||||||
|
// Handle selected rows change.
|
||||||
|
const handleSelectedRowsChange = useCallback(
|
||||||
|
(payment) => {
|
||||||
|
setSelectedRows(payment);
|
||||||
|
},
|
||||||
|
[setSelectedRows],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
// loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
|
||||||
|
name={'payment_made'}
|
||||||
|
>
|
||||||
|
<PaymentMadeActionsBar
|
||||||
|
// onBulkDelete={}
|
||||||
|
selectedRows={selectedRows}
|
||||||
|
onFilterChanged={handleFilterChanged}
|
||||||
|
/>
|
||||||
|
<DashboardPageContent>
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
exact={true}
|
||||||
|
path={[
|
||||||
|
'/payment-mades/:custom_view_id/custom_view',
|
||||||
|
'/payment-mades',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<PaymentMadeViewTabs />
|
||||||
|
<PaymentMadeDataTable
|
||||||
|
loading={fetchPaymentMades.isFetching}
|
||||||
|
onDeletePaymentMade={handleDeletePaymentMade}
|
||||||
|
onFetchData={handleFetchData}
|
||||||
|
onEditPaymentMade={handleEditPaymentMade}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
<Alert
|
||||||
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
|
confirmButtonText={<T id={'delete'} />}
|
||||||
|
icon={'trash'}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
isOpen={deletePaymentMade}
|
||||||
|
onCancel={handleCancelPaymentMadeDelete}
|
||||||
|
onConfirm={handleConfirmPaymentMadeDelete}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<T
|
||||||
|
id={'once_delete_this_payment_made_you_will_able_to_restore_it'}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
|
</DashboardPageContent>
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withResourceActions,
|
||||||
|
withPaymentMadeActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withViewsActions,
|
||||||
|
withPaymentMades(({ paymentMadeTableQuery }) => ({
|
||||||
|
paymentMadeTableQuery,
|
||||||
|
})),
|
||||||
|
)(PaymentMadeList);
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
|
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||||
|
import { useParams, withRouter } from 'react-router-dom';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { pick, debounce } from 'lodash';
|
||||||
|
|
||||||
|
import { DashboardViewsTabs } from 'components';
|
||||||
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import withPaymentMade from './withPaymentMade';
|
||||||
|
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withViewDetails from 'containers/Views/withViewDetails';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentMadeViewTabs({
|
||||||
|
//#withPaymentMades
|
||||||
|
paymentMadeViews,
|
||||||
|
|
||||||
|
//#withPaymentMadesActions
|
||||||
|
changePaymentMadeView,
|
||||||
|
addPaymentMadesTableQueries,
|
||||||
|
|
||||||
|
// #withViewDetails
|
||||||
|
viewItem,
|
||||||
|
|
||||||
|
// #withDashboardActions
|
||||||
|
setTopbarEditView,
|
||||||
|
changePageSubtitle,
|
||||||
|
|
||||||
|
//#Own Props
|
||||||
|
onViewChanged,
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { custom_view_id: customViewId = null } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changePaymentMadeView(customViewId || -1);
|
||||||
|
setTopbarEditView(customViewId);
|
||||||
|
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||||
|
|
||||||
|
addPaymentMadesTableQueries({
|
||||||
|
custom_view_id: customViewId,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
setTopbarEditView(null);
|
||||||
|
changePageSubtitle('');
|
||||||
|
changePaymentMadeView(null);
|
||||||
|
};
|
||||||
|
}, [customViewId, addPaymentMadesTableQueries, changePaymentMadeView]);
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
onViewChanged && onViewChanged(customViewId);
|
||||||
|
}, [customViewId]);
|
||||||
|
|
||||||
|
const debounceChangeHistory = useRef(
|
||||||
|
debounce((toUrl) => {
|
||||||
|
history.push(toUrl);
|
||||||
|
}, 250),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTabsChange = (viewId) => {
|
||||||
|
const toPath = viewId ? `${viewId}/custom_view` : '';
|
||||||
|
debounceChangeHistory.current(`/payment-mades/${toPath}`);
|
||||||
|
setTopbarEditView(viewId);
|
||||||
|
};
|
||||||
|
const tabs = paymentMadeViews.map((view) => ({
|
||||||
|
...pick(view, ['name', 'id']),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const handleClickNewView = () => {
|
||||||
|
setTopbarEditView(null);
|
||||||
|
history.push('/custom_views/payment-mades/new');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Navbar className={'navbar--dashboard-views'}>
|
||||||
|
<NavbarGroup align={Alignment.LEFT}>
|
||||||
|
<DashboardViewsTabs
|
||||||
|
initialViewId={customViewId}
|
||||||
|
baseUrl={'/payment-mades'}
|
||||||
|
tabs={tabs}
|
||||||
|
onNewViewTabClick={handleClickNewView}
|
||||||
|
onChange={handleTabsChange}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</Navbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
viewId: ownProps.match.params.custom_view_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withPaymentMadesViewTabs = connect(mapStateToProps);
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
withPaymentMadesViewTabs,
|
||||||
|
withPaymentMadeActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withViewDetails(),
|
||||||
|
withPaymentMade(({ paymentMadeViews }) => ({
|
||||||
|
paymentMadeViews,
|
||||||
|
})),
|
||||||
|
)(PaymentMadeViewTabs);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
|
import {
|
||||||
|
getPaymentMadeCurrentPageFactory,
|
||||||
|
getPaymentMadePaginationMetaFactory,
|
||||||
|
getPaymentMadeTableQuery,
|
||||||
|
} from 'store/PaymentMades/paymentMade.selector';
|
||||||
|
|
||||||
|
|
||||||
|
export default (mapState) => {
|
||||||
|
const getPyamentMadesItems = getPaymentMadeCurrentPageFactory();
|
||||||
|
const getPyamentMadesPaginationMeta = getPaymentMadePaginationMetaFactory();
|
||||||
|
const mapStateToProps = (state, props) => {
|
||||||
|
const query = getPaymentMadeTableQuery(state, props);
|
||||||
|
const mapped = {
|
||||||
|
paymentMadeCurrentPage: getPyamentMadesItems(state, props, query),
|
||||||
|
paymentMadeViews: getResourceViews(state, props, 'bill_payments'),
|
||||||
|
paymentMadeItems: state.paymentMades.items,
|
||||||
|
paymentMadeTableQuery: query,
|
||||||
|
paymentMadePageination: getPyamentMadesPaginationMeta(state, props, query),
|
||||||
|
paymentMadesLoading: state.paymentMades.loading,
|
||||||
|
};
|
||||||
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
|
};
|
||||||
|
return connect(mapStateToProps);
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import t from 'store/types';
|
||||||
|
import {
|
||||||
|
submitPaymentMade,
|
||||||
|
editPaymentMade,
|
||||||
|
deletePaymentMade,
|
||||||
|
fetchPaymentMadesTable,
|
||||||
|
fetchPaymentMade,
|
||||||
|
} from 'store/PaymentMades/paymentMade.actions';
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
requestSubmitPaymentMade: (form) => dispatch(submitPaymentMade({ form })),
|
||||||
|
requestFetchPaymentMade: (id) => dispatch(fetchPaymentMade({ id })),
|
||||||
|
requestEditPaymentMade: (id, form) => dispatch(editPaymentMade(id, form)),
|
||||||
|
requestDeletePaymentMade: (id) => dispatch(deletePaymentMade({ id })),
|
||||||
|
requestFetchPaymentMadesTable: (query = {}) =>
|
||||||
|
dispatch(fetchPaymentMadesTable({ query: { ...query } })),
|
||||||
|
|
||||||
|
changePaymentMadeView: (id) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADE_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
addPaymentMadesTableQueries: (queries) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADE_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export default connect(null, mapDispatchToProps);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getPaymentMadeByIdFactory } from 'store/PaymentMades/paymentMade.selector';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const getPaymentMadeById = getPaymentMadeByIdFactory();
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
paymentMade: getPaymentMadeById(state, props),
|
||||||
|
});
|
||||||
|
return connect(mapStateToProps);
|
||||||
|
};
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import {
|
|
||||||
submitBill,
|
|
||||||
deleteBill,
|
|
||||||
editBill,
|
|
||||||
fetchBillsTable,
|
|
||||||
fetchBill,
|
|
||||||
} from 'store/Bills/bills.actions';
|
|
||||||
import t from 'store/types';
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
requestSubmitBill: (form) => dispatch(submitBill({ form })),
|
|
||||||
requestFetchBill: (id) => dispatch(fetchBill({ id })),
|
|
||||||
requestEditBill: (id, form) => dispatch(editBill({ id, form })),
|
|
||||||
requestDeleteBill: (id) => dispatch(deleteBill({ id })),
|
|
||||||
requestFetchBillsTable: (query = {}) =>
|
|
||||||
dispatch(fetchBillsTable({ query: { ...query } })),
|
|
||||||
|
|
||||||
changeBillView: (id) =>
|
|
||||||
dispatch({
|
|
||||||
type: t.BILL_SET_CURRENT_VIEW,
|
|
||||||
currentViewId: parseInt(id, 10),
|
|
||||||
}),
|
|
||||||
|
|
||||||
addBillsTableQueries: (queries) =>
|
|
||||||
dispatch({
|
|
||||||
type: t.BILLS_TABLE_QUERIES_ADD,
|
|
||||||
queries,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps);
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
|
import { omit } from 'lodash';
|
||||||
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import CLASSES from 'components/classes';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
|
|
||||||
import { compose, formattedAmount } from 'utils';
|
|
||||||
import {
|
import {
|
||||||
InputGroupCell,
|
InputGroupCell,
|
||||||
MoneyFieldCell,
|
MoneyFieldCell,
|
||||||
@@ -14,7 +15,7 @@ import {
|
|||||||
} from 'components/DataTableCells';
|
} from 'components/DataTableCells';
|
||||||
|
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
import { omit } from 'lodash';
|
import { compose, formattedAmount } from 'utils';
|
||||||
|
|
||||||
const ActionsCellRenderer = ({
|
const ActionsCellRenderer = ({
|
||||||
row: { index },
|
row: { index },
|
||||||
@@ -92,6 +93,7 @@ function EstimateTable({
|
|||||||
width: 40,
|
width: 40,
|
||||||
disableResizing: true,
|
disableResizing: true,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
className: 'index',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'product_and_service' }),
|
Header: formatMessage({ id: 'product_and_service' }),
|
||||||
@@ -108,6 +110,7 @@ function EstimateTable({
|
|||||||
Cell: InputGroupCell,
|
Cell: InputGroupCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: 'description',
|
className: 'description',
|
||||||
|
width: 120,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -123,7 +126,7 @@ function EstimateTable({
|
|||||||
accessor: 'rate',
|
accessor: 'rate',
|
||||||
Cell: TotalEstimateCellRederer(MoneyFieldCell, 'rate'),
|
Cell: TotalEstimateCellRederer(MoneyFieldCell, 'rate'),
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 150,
|
width: 100,
|
||||||
className: 'rate',
|
className: 'rate',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -230,8 +233,9 @@ function EstimateTable({
|
|||||||
updateData: handleUpdateData,
|
updateData: handleUpdateData,
|
||||||
removeRow: handleRemoveRow,
|
removeRow: handleRemoveRow,
|
||||||
}}
|
}}
|
||||||
|
className={CLASSES.DATATABLE_EDITOR}
|
||||||
/>
|
/>
|
||||||
<div className={'mt1'}>
|
<div className={'datatable-editor-actions mt1'}>
|
||||||
<Button
|
<Button
|
||||||
small={true}
|
small={true}
|
||||||
className={'button--secondary button--new-line'}
|
className={'button--secondary button--new-line'}
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ function EstimateActionsBar({
|
|||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
resourceName: 'sales_estimates',
|
resourceName: 'sales_estimates',
|
||||||
});
|
});
|
||||||
|
|
||||||
const withEstimateActionsBar = connect(mapStateToProps);
|
const withEstimateActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Intent, FormGroup, TextArea, Button } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick, omit } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { queryCache } from 'react-query';
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
|
||||||
import EstimateFormHeader from './EstimateFormHeader';
|
import EstimateFormHeader from './EstimateFormHeader';
|
||||||
import EstimatesItemsTable from './EntriesItemsTable';
|
import EstimatesItemsTable from './EntriesItemsTable';
|
||||||
@@ -103,13 +102,11 @@ const EstimateForm = ({
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(1024)
|
.max(1024)
|
||||||
.label(formatMessage({ id: 'note' })),
|
.label(formatMessage({ id: 'note' })),
|
||||||
|
|
||||||
terms_conditions: Yup.string()
|
terms_conditions: Yup.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(1024)
|
.max(1024)
|
||||||
.label(formatMessage({ id: 'note' })),
|
.label(formatMessage({ id: 'note' })),
|
||||||
|
|
||||||
entries: Yup.array().of(
|
entries: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
quantity: Yup.number().nullable(),
|
quantity: Yup.number().nullable(),
|
||||||
@@ -176,7 +173,7 @@ const EstimateForm = ({
|
|||||||
index: index + 1,
|
index: index + 1,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
// debugger;
|
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...(estimate
|
...(estimate
|
||||||
@@ -200,14 +197,6 @@ const EstimateForm = ({
|
|||||||
[estimate, defaultInitialValues, defaultEstimate],
|
[estimate, defaultInitialValues, defaultEstimate],
|
||||||
);
|
);
|
||||||
|
|
||||||
// const initialValues = useMemo(
|
|
||||||
// () => ({
|
|
||||||
// ...defaultInitialValues,
|
|
||||||
// entries: orderingProductsIndex(defaultInitialValues.entries),
|
|
||||||
// }),
|
|
||||||
// [defaultEstimate, defaultInitialValues, estimate],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return estimate && estimate.media
|
return estimate && estimate.media
|
||||||
? estimate.media.map((attach) => ({
|
? estimate.media.map((attach) => ({
|
||||||
@@ -251,12 +240,8 @@ const EstimateForm = ({
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage(
|
message: formatMessage(
|
||||||
{
|
{ id: 'the_estimate_has_been_successfully_created' },
|
||||||
id: 'the_estimate_has_been_successfully_created',
|
{ number: values.estimate_number },
|
||||||
},
|
|
||||||
{
|
|
||||||
number: values.estimate_number,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
@@ -270,7 +255,7 @@ const EstimateForm = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
console.log(formik.errors ,'ERROR');
|
||||||
const handleSubmitClick = useCallback(
|
const handleSubmitClick = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
setPayload(payload);
|
setPayload(payload);
|
||||||
@@ -313,9 +298,6 @@ const EstimateForm = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@@ -325,36 +307,45 @@ const EstimateForm = ({
|
|||||||
onClickAddNewRow={handleClickAddNewRow}
|
onClickAddNewRow={handleClickAddNewRow}
|
||||||
onClickClearAllLines={handleClearAllLines}
|
onClickClearAllLines={handleClearAllLines}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
// defaultRow={defaultEstimate}
|
|
||||||
/>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'customer_note'} />}
|
|
||||||
className={'form-group--customer_note'}
|
|
||||||
>
|
|
||||||
<TextArea growVertically={true} {...formik.getFieldProps('note')} />
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'terms_conditions'} />}
|
|
||||||
className={'form-group--terms_conditions'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('terms_conditions')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<Dragzone
|
|
||||||
initialFiles={initialAttachmentFiles}
|
|
||||||
onDrop={handleDropFiles}
|
|
||||||
onDeleteFile={handleDeleteFile}
|
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'customer_note'} />}
|
||||||
|
className={'form-group--customer_note'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('note')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'terms_conditions'} />}
|
||||||
|
className={'form-group--terms_conditions'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('terms_conditions')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</form>
|
</form>
|
||||||
<EstimateFormFooter
|
<EstimateFormFooter
|
||||||
formik={formik}
|
formik={formik}
|
||||||
onSubmitClick={handleSubmitClick}
|
onSubmitClick={handleSubmitClick}
|
||||||
estimate={estimate}
|
estimate={estimate}
|
||||||
onCancelClick={handleCancelClick}
|
onCancelClick={handleCancelClick}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function EstimateFormFooter({
|
|||||||
estimate,
|
estimate,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form__floating-footer'}>
|
<div className={'form__floating-footer'}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
|
|||||||
@@ -68,13 +68,16 @@ function EstimateFormHeader({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form'}>
|
<div className={'page-form page-form--estimate'}>
|
||||||
<div className={'estimate-form__primary-section'}>
|
<div className={'page-form__primary-section'}>
|
||||||
{/* customer name */}
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames(
|
||||||
|
'form-group--select-list',
|
||||||
|
'form-group--customer',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
@@ -94,17 +97,18 @@ function EstimateFormHeader({
|
|||||||
labelProp={'display_name'}
|
labelProp={'display_name'}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{/* estimate_date */}
|
|
||||||
<Row>
|
|
||||||
<Col
|
|
||||||
|
|
||||||
// md={9} push={{ md: 3 }}
|
<Row>
|
||||||
>
|
<Col>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'estimate_date'} />}
|
label={<T id={'estimate_date'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames(
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
'form-group--estimate-date',
|
||||||
|
)}
|
||||||
intent={
|
intent={
|
||||||
errors.estimate_date && touched.estimate_date && Intent.DANGER
|
errors.estimate_date && touched.estimate_date && Intent.DANGER
|
||||||
}
|
}
|
||||||
@@ -120,14 +124,15 @@ function EstimateFormHeader({
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
<Col
|
<Col>
|
||||||
|
|
||||||
// md={3} pull={{ md: 9 }}
|
|
||||||
>
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'expiration_date'} />}
|
label={<T id={'expiration_date'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames(
|
||||||
|
'form-group--select-list',
|
||||||
|
'form-group--expiration-date',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
intent={
|
intent={
|
||||||
errors.expiration_date &&
|
errors.expiration_date &&
|
||||||
touched.expiration_date &&
|
touched.expiration_date &&
|
||||||
@@ -147,11 +152,12 @@ function EstimateFormHeader({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
{/* Estimate */}
|
|
||||||
|
{/*- Estimate -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'estimate'} />}
|
label={<T id={'estimate'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={('form-group--estimate', Classes.FILL)}
|
className={('form-group--estimate-number', Classes.FILL)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={
|
intent={
|
||||||
errors.estimate_number && touched.estimate_number && Intent.DANGER
|
errors.estimate_number && touched.estimate_number && Intent.DANGER
|
||||||
@@ -168,6 +174,7 @@ function EstimateFormHeader({
|
|||||||
{...getFieldProps('estimate_number')}
|
{...getFieldProps('estimate_number')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ function Estimates({
|
|||||||
requestFetchCustomers({}),
|
requestFetchCustomers({}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//
|
||||||
const handleFormSubmit = useCallback(
|
const handleFormSubmit = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
payload.redirect && history.push('/estimates');
|
payload.redirect && history.push('/estimates');
|
||||||
|
|||||||
@@ -2,22 +2,25 @@ import { connect } from 'react-redux';
|
|||||||
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
import {
|
import {
|
||||||
getEstimateCurrentPageFactory,
|
getEstimateCurrentPageFactory,
|
||||||
getEstimatesTableQuery,
|
getEstimatesTableQueryFactory,
|
||||||
getEstimatesPaginationMetaFactory,
|
getEstimatesPaginationMetaFactory,
|
||||||
} from 'store/Estimate/estimates.selectors';
|
} from 'store/Estimate/estimates.selectors';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getEstimatesItems = getEstimateCurrentPageFactory();
|
const getEstimatesItems = getEstimateCurrentPageFactory();
|
||||||
const getEstimatesPaginationMeta = getEstimatesPaginationMetaFactory();
|
const getEstimatesPaginationMeta = getEstimatesPaginationMetaFactory();
|
||||||
|
const getEstimatesTableQuery = getEstimatesTableQueryFactory();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const query = getEstimatesTableQuery(state, props);
|
const query = getEstimatesTableQuery(state, props);
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
estimatesCurrentPage: getEstimatesItems(state, props, query),
|
estimatesCurrentPage: getEstimatesItems(state, props, query),
|
||||||
estimateViews: getResourceViews(state, props, 'sales_estimates'),
|
estimateViews: getResourceViews(state, props, 'sales_estimates'),
|
||||||
estimateItems: state.sales_estimates.items,
|
estimateItems: state.salesEstimates.items,
|
||||||
estimateTableQuery: query,
|
estimateTableQuery: query,
|
||||||
estimatesPageination: getEstimatesPaginationMeta(state, props, query),
|
estimatesPageination: getEstimatesPaginationMeta(state, props, query),
|
||||||
estimatesLoading: state.sales_estimates.loading,
|
estimatesLoading: state.salesEstimates.loading,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import moment from 'moment';
|
|||||||
import { Intent, FormGroup, TextArea, Button } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea, Button } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
|
||||||
import InvoiceFormHeader from './InvoiceFormHeader';
|
import InvoiceFormHeader from './InvoiceFormHeader';
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
@@ -162,7 +163,7 @@ function InvoiceForm({
|
|||||||
index: index + 1,
|
index: index + 1,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
// debugger;
|
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...(invoice
|
...(invoice
|
||||||
@@ -186,14 +187,6 @@ function InvoiceForm({
|
|||||||
[invoice, defaultInitialValues, defaultInvoice],
|
[invoice, defaultInitialValues, defaultInvoice],
|
||||||
);
|
);
|
||||||
|
|
||||||
// const initialValues = useMemo(
|
|
||||||
// () => ({
|
|
||||||
// ...defaultInitialValues,
|
|
||||||
// entries: orderingIndex(defaultInitialValues.entries),
|
|
||||||
// }),
|
|
||||||
// [defaultInvoice, defaultInitialValues, invoice],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return invoice && invoice.media
|
return invoice && invoice.media
|
||||||
? invoice.media.map((attach) => ({
|
? invoice.media.map((attach) => ({
|
||||||
@@ -308,31 +301,39 @@ function InvoiceForm({
|
|||||||
onClickClearAllLines={handleClearAllLines}
|
onClickClearAllLines={handleClearAllLines}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<Row>
|
||||||
label={<T id={'invoice_message'} />}
|
<Col>
|
||||||
className={'form-group--customer_note'}
|
<FormGroup
|
||||||
>
|
label={<T id={'invoice_message'} />}
|
||||||
<TextArea
|
className={'form-group--customer_note'}
|
||||||
growVertically={true}
|
>
|
||||||
{...formik.getFieldProps('invoice_message')}
|
<TextArea
|
||||||
/>
|
growVertically={true}
|
||||||
</FormGroup>
|
{...formik.getFieldProps('invoice_message')}
|
||||||
<FormGroup
|
/>
|
||||||
label={<T id={'terms_conditions'} />}
|
</FormGroup>
|
||||||
className={'form-group--terms_conditions'}
|
<FormGroup
|
||||||
>
|
label={<T id={'terms_conditions'} />}
|
||||||
<TextArea
|
className={'form-group--terms_conditions'}
|
||||||
growVertically={true}
|
>
|
||||||
{...formik.getFieldProps('terms_conditions')}
|
<TextArea
|
||||||
/>
|
growVertically={true}
|
||||||
</FormGroup>
|
{...formik.getFieldProps('terms_conditions')}
|
||||||
<Dragzone
|
/>
|
||||||
initialFiles={initialAttachmentFiles}
|
</FormGroup>
|
||||||
onDrop={handleDropFiles}
|
</Col>
|
||||||
onDeleteFile={handleDeleteFile}
|
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
<Col>
|
||||||
/>
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<InvoiceFormFooter
|
<InvoiceFormFooter
|
||||||
formik={formik}
|
formik={formik}
|
||||||
onSubmitClick={handleSubmitClick}
|
onSubmitClick={handleSubmitClick}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default function EstimateFormFooter({
|
|||||||
invoice,
|
invoice,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form__floating-footer'}>
|
<div className={'form__floating-footer'}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ function InvoiceFormHeader({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'invoice-form'}>
|
<div class="page-form page-form--invoice">
|
||||||
<div className={'invoice__primary-section'}>
|
<div className={'page-form__primary-section'}>
|
||||||
{/* customer name */}
|
{/* customer name */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ function Invoices({
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const fetchInvoice = useQuery(
|
||||||
|
['invoice', id],
|
||||||
|
(key, _id) => requsetFetchInvoice(_id),
|
||||||
|
{ enabled: !!id },
|
||||||
|
);
|
||||||
|
|
||||||
// Handle fetch Items data table or list
|
// Handle fetch Items data table or list
|
||||||
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
||||||
|
|
||||||
@@ -33,12 +39,6 @@ function Invoices({
|
|||||||
requestFetchCustomers({}),
|
requestFetchCustomers({}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchInvoice = useQuery(
|
|
||||||
['invoice', id],
|
|
||||||
(key, _id) => requsetFetchInvoice(_id),
|
|
||||||
{ enabled: !!id },
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|||||||
@@ -5,17 +5,18 @@ import {
|
|||||||
deleteInvoice,
|
deleteInvoice,
|
||||||
fetchInvoice,
|
fetchInvoice,
|
||||||
fetchInvoicesTable,
|
fetchInvoicesTable,
|
||||||
|
dueInvoices,
|
||||||
} from 'store/Invoice/invoices.actions';
|
} from 'store/Invoice/invoices.actions';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
const mapDipatchToProps = (dispatch) => ({
|
const mapDipatchToProps = (dispatch) => ({
|
||||||
requestSubmitInvoice: (form) => dispatch(submitInvoice({ form })),
|
requestSubmitInvoice: (form) => dispatch(submitInvoice({ form })),
|
||||||
requsetFetchInvoice: (id) => dispatch(fetchInvoice({ id })),
|
requsetFetchInvoice: (id) => dispatch(fetchInvoice({ id })),
|
||||||
requestEditInvoice: (id, form) => dispatch(editInvoice( id, form )),
|
requestEditInvoice: (id, form) => dispatch(editInvoice(id, form)),
|
||||||
requestFetchInvoiceTable: (query = {}) =>
|
requestFetchInvoiceTable: (query = {}) =>
|
||||||
dispatch(fetchInvoicesTable({ query: { ...query } })),
|
dispatch(fetchInvoicesTable({ query: { ...query } })),
|
||||||
requestDeleteInvoice: (id) => dispatch(deleteInvoice({ id })),
|
requestDeleteInvoice: (id) => dispatch(deleteInvoice({ id })),
|
||||||
|
requestFetchDueInvoices: (id) => dispatch(dueInvoices({ id })),
|
||||||
changeInvoiceView: (id) =>
|
changeInvoiceView: (id) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.INVOICES_SET_CURRENT_VIEW,
|
type: t.INVOICES_SET_CURRENT_VIEW,
|
||||||
|
|||||||
@@ -3,21 +3,28 @@ import { getResourceViews } from 'store/customViews/customViews.selectors';
|
|||||||
import {
|
import {
|
||||||
getInvoiceCurrentPageFactory,
|
getInvoiceCurrentPageFactory,
|
||||||
getInvoicePaginationMetaFactory,
|
getInvoicePaginationMetaFactory,
|
||||||
|
getInvoiceTableQueryFactory,
|
||||||
getInvoiceTableQuery,
|
getInvoiceTableQuery,
|
||||||
|
getdueInvoices,
|
||||||
} from 'store/Invoice/invoices.selector';
|
} from 'store/Invoice/invoices.selector';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getInvoicesItems = getInvoiceCurrentPageFactory();
|
const getInvoicesItems = getInvoiceCurrentPageFactory();
|
||||||
|
|
||||||
const getInvoicesPaginationMeta = getInvoicePaginationMetaFactory();
|
const getInvoicesPaginationMeta = getInvoicePaginationMetaFactory();
|
||||||
|
const getInvoiceTableQuery = getInvoiceTableQueryFactory();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const query = getInvoiceTableQuery(state, props);
|
const query = getInvoiceTableQuery(state, props);
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
invoicesCurrentPage: getInvoicesItems(state, props, query),
|
invoicesCurrentPage: getInvoicesItems(state, props, query),
|
||||||
invoicesViews: getResourceViews(state, props, 'sales_invoices'),
|
invoicesViews: getResourceViews(state, props, 'sales_invoices'),
|
||||||
invoicesItems: state.sales_invoices.items,
|
invoicesItems: state.salesInvoices.items,
|
||||||
invoicesTableQuery: query,
|
invoicesTableQuery: query,
|
||||||
invoicesPageination: getInvoicesPaginationMeta(state, props, query),
|
invoicesPageination: getInvoicesPaginationMeta(state, props, query),
|
||||||
invoicesLoading: state.sales_invoices.loading,
|
invoicesLoading: state.salesInvoices.loading,
|
||||||
|
dueInvoices: getdueInvoices(state, props),
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import React, { useCallback, useState, useMemo } from 'react';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Popover,
|
||||||
|
NavbarDivider,
|
||||||
|
NavbarGroup,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
Position,
|
||||||
|
Intent,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
|
|
||||||
|
import { If, DashboardActionViewsList } from 'components';
|
||||||
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
|
||||||
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withPaymentReceives from './withPaymentReceives';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentReceiveActionsBar({
|
||||||
|
// #withResourceDetail
|
||||||
|
resourceFields,
|
||||||
|
|
||||||
|
//#withPaymentReceives
|
||||||
|
paymentReceivesViews,
|
||||||
|
|
||||||
|
//#withPaymentReceivesActions
|
||||||
|
addPaymentReceivesTableQueries,
|
||||||
|
|
||||||
|
// #own Porps
|
||||||
|
onFilterChanged,
|
||||||
|
selectedRows = [],
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { path } = useRouteMatch();
|
||||||
|
const [filterCount, setFilterCount] = useState(0);
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const handleClickNewPaymentReceive = useCallback(() => {
|
||||||
|
history.push('/payment-receive/new');
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
// const filterDropdown = FilterDropdown({
|
||||||
|
// initialCondition: {
|
||||||
|
// fieldKey: '',
|
||||||
|
// compatator: 'contains',
|
||||||
|
// value: '',
|
||||||
|
// },
|
||||||
|
// fields: resourceFields,
|
||||||
|
// onFilterChange: (filterConditions) => {
|
||||||
|
// addPaymentReceivesTableQueries({
|
||||||
|
// filter_roles: filterConditions || '',
|
||||||
|
// });
|
||||||
|
// onFilterChanged && onFilterChanged(filterConditions);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||||
|
selectedRows,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardActionsBar>
|
||||||
|
<NavbarGroup>
|
||||||
|
<DashboardActionViewsList
|
||||||
|
resourceName={'payment_receives'}
|
||||||
|
views={paymentReceivesViews}
|
||||||
|
/>
|
||||||
|
<NavbarDivider />
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'plus'} />}
|
||||||
|
text={<T id={'new_payment_receive'} />}
|
||||||
|
onClick={handleClickNewPaymentReceive}
|
||||||
|
/>
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
// content={filterDropdown}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.BOTTOM_LEFT}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={classNames(Classes.MINIMAL)}
|
||||||
|
text={
|
||||||
|
filterCount <= 0 ? (
|
||||||
|
<T id={'filter'} />
|
||||||
|
) : (
|
||||||
|
`${filterCount} ${formatMessage({ id: 'filters_applied' })}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
icon={<Icon icon={'filter-16'} iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
<If condition={hasSelectedRows}>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||||
|
text={<T id={'delete'} />}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
// onClick={handleBulkDelete}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'print-16'} iconSize={'16'} />}
|
||||||
|
text={<T id={'print'} />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'file-import-16'} />}
|
||||||
|
text={<T id={'import'} />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'file-export-16'} iconSize={'16'} />}
|
||||||
|
text={<T id={'export'} />}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</DashboardActionsBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
resourceName: 'payment_receives',
|
||||||
|
});
|
||||||
|
const withPaymentReceiveActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withPaymentReceiveActionsBar,
|
||||||
|
withResourceDetail(({ resourceFields }) => ({
|
||||||
|
resourceFields,
|
||||||
|
})),
|
||||||
|
withPaymentReceives(({ paymentReceivesViews }) => ({ paymentReceivesViews })),
|
||||||
|
)(PaymentReceiveActionsBar);
|
||||||
@@ -1,51 +1,65 @@
|
|||||||
import React, { useMemo, useCallback, useEffect, useState,useRef } from 'react';
|
import React, {
|
||||||
|
useMemo,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import { pick, values } from 'lodash';
|
||||||
|
|
||||||
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
||||||
// PaymentReceiptItemsTable
|
import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
|
||||||
import PaymentReceiveFooter from './PaymentReceiveFormFooter';
|
import PaymentReceiveFooter from './PaymentReceiveFormFooter';
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
import withPaymentReceivesActions from './withPaymentReceivesActions'
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withInvoices from '../Invoice/withInvoices';
|
||||||
|
import withPaymentReceiveDetail from './withPaymentReceiveDetail';
|
||||||
|
import withPaymentReceives from './withPaymentReceives';
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
import { compose, repeatValue } from 'utils';
|
import { compose, repeatValue } from 'utils';
|
||||||
|
|
||||||
const MIN_LINES_NUMBER = 4;
|
const MIN_LINES_NUMBER = 5;
|
||||||
|
|
||||||
function PaymentReceiveForm({
|
function PaymentReceiveForm({
|
||||||
//#withMedia
|
//#withMedia
|
||||||
requestSubmitMedia,
|
requestSubmitMedia,
|
||||||
requestDeleteMedia,
|
requestDeleteMedia,
|
||||||
|
|
||||||
//#WithPaymentReceiveActions
|
//#WithPaymentReceiveActions
|
||||||
requestSubmitPaymentReceive,
|
requestSubmitPaymentReceive,
|
||||||
|
requestEditPaymentReceive,
|
||||||
|
|
||||||
//#withDashboard
|
//#withDashboard
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
|
|
||||||
//#withPaymentReceiveDetail
|
//#withPaymentReceiveDetail
|
||||||
|
paymentReceive,
|
||||||
|
paymentReceiveInvoices,
|
||||||
|
paymentReceivesItems,
|
||||||
|
|
||||||
//#OWn Props
|
//#OWn Props
|
||||||
payment_receive,
|
// payment_receive,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onCancelForm,
|
onCancelForm,
|
||||||
|
dueInvoiceLength,
|
||||||
|
onCustomerChange,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [payload, setPayload] = useState({});
|
const [payload, setPayload] = useState({});
|
||||||
|
const { id } = useParams();
|
||||||
const {
|
const {
|
||||||
setFiles,
|
setFiles,
|
||||||
saveMedia,
|
saveMedia,
|
||||||
@@ -63,43 +77,48 @@ function PaymentReceiveForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (payment_receive && payment_receive.id) {
|
onCustomerChange && onCustomerChange(formik.values.customer_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (paymentReceive && paymentReceive.id) {
|
||||||
changePageTitle(formatMessage({ id: 'edit_payment_receive' }));
|
changePageTitle(formatMessage({ id: 'edit_payment_receive' }));
|
||||||
} else {
|
} else {
|
||||||
changePageTitle(formatMessage({ id: 'new_payment_receive' }));
|
changePageTitle(formatMessage({ id: 'payment_receive' }));
|
||||||
}
|
}
|
||||||
}, [changePageTitle, payment_receive, formatMessage]);
|
}, [changePageTitle, paymentReceive, formatMessage]);
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
customer_id: Yup.string()
|
customer_id: Yup.string()
|
||||||
.label(formatMessage({ id: 'customer_name_' }))
|
.label(formatMessage({ id: 'customer_name_' }))
|
||||||
.required(),
|
.required(),
|
||||||
deposit_account_id: Yup.number()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'deposit_account_' })),
|
|
||||||
payment_date: Yup.date()
|
payment_date: Yup.date()
|
||||||
.required()
|
.required()
|
||||||
.label(formatMessage({ id: 'payment_date_' })),
|
.label(formatMessage({ id: 'payment_date_' })),
|
||||||
|
deposit_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'deposit_account_' })),
|
||||||
|
// receive_amount: Yup.number()
|
||||||
|
// .required()
|
||||||
|
// .label(formatMessage({ id: 'receive_amount_' })),
|
||||||
payment_receive_no: Yup.number()
|
payment_receive_no: Yup.number()
|
||||||
.required()
|
.required()
|
||||||
.label(formatMessage({ id: 'payment_receive_no_' })),
|
.label(formatMessage({ id: 'payment_receive_no_' })),
|
||||||
reference_no: Yup.string().min(1).max(255),
|
reference_no: Yup.string().min(1).max(255).nullable(),
|
||||||
statement: Yup.string()
|
description: Yup.string().nullable(),
|
||||||
.trim()
|
|
||||||
.min(1)
|
|
||||||
.max(1024)
|
|
||||||
.label(formatMessage({ id: 'statement' })),
|
|
||||||
|
|
||||||
entries: Yup.array().of(
|
entries: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
payment_amount: Yup.number().nullable(),
|
payment_amount: Yup.number().nullable(),
|
||||||
item_id: Yup.number()
|
invoice_no: Yup.number().nullable(),
|
||||||
|
balance: Yup.number().nullable(),
|
||||||
|
due_amount: Yup.number().nullable(),
|
||||||
|
invoice_date: Yup.date(),
|
||||||
|
invoice_id: Yup.number()
|
||||||
.nullable()
|
.nullable()
|
||||||
.when(['payment_amount'], {
|
.when(['payment_amount'], {
|
||||||
is: (payment_amount) => payment_amount,
|
is: (payment_amount) => payment_amount,
|
||||||
then: Yup.number().required(),
|
then: Yup.number().required(),
|
||||||
}),
|
}),
|
||||||
description: Yup.string().nullable(),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@@ -114,9 +133,12 @@ function PaymentReceiveForm({
|
|||||||
|
|
||||||
const defaultPaymentReceive = useMemo(
|
const defaultPaymentReceive = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
item_id: null,
|
invoice_id: '',
|
||||||
payment_amount: null,
|
invoice_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
description: null,
|
invoice_no: '',
|
||||||
|
balance: '',
|
||||||
|
due_amount: '',
|
||||||
|
payment_amount: '',
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -126,29 +148,53 @@ function PaymentReceiveForm({
|
|||||||
deposit_account_id: '',
|
deposit_account_id: '',
|
||||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
reference_no: '',
|
reference_no: '',
|
||||||
statement: '',
|
payment_receive_no: '',
|
||||||
|
// receive_amount: '',
|
||||||
|
description: '',
|
||||||
entries: [...repeatValue(defaultPaymentReceive, MIN_LINES_NUMBER)],
|
entries: [...repeatValue(defaultPaymentReceive, MIN_LINES_NUMBER)],
|
||||||
}),
|
}),
|
||||||
[defaultPaymentReceive],
|
[defaultPaymentReceive],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const orderingIndex = (_entries) => {
|
||||||
|
return _entries.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
index: index + 1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...defaultInitialValues,
|
...(paymentReceive
|
||||||
entries: defaultInitialValues.entries,
|
? {
|
||||||
|
...pick(paymentReceive, Object.keys(defaultInitialValues)),
|
||||||
|
entries: [
|
||||||
|
...paymentReceive.entries.map((paymentReceive) => ({
|
||||||
|
...pick(paymentReceive, Object.keys(defaultPaymentReceive)),
|
||||||
|
})),
|
||||||
|
...repeatValue(
|
||||||
|
defaultPaymentReceive,
|
||||||
|
Math.max(MIN_LINES_NUMBER - paymentReceive.entries.length, 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...defaultInitialValues,
|
||||||
|
entries: orderingIndex(defaultInitialValues.entries),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
[defaultPaymentReceive, defaultInitialValues, payment_receive],
|
[paymentReceive, defaultInitialValues, defaultPaymentReceive],
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return payment_receive && payment_receive.media
|
return paymentReceive && paymentReceive.media
|
||||||
? payment_receive.media.map((attach) => ({
|
? paymentReceive.media.map((attach) => ({
|
||||||
preview: attach.attachment_file,
|
preview: attach.attachment_file,
|
||||||
uploaded: true,
|
uploaded: true,
|
||||||
metadata: { ...attach },
|
metadata: { ...attach },
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
}, [payment_receive]);
|
}, [paymentReceive]);
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
@@ -157,38 +203,52 @@ function PaymentReceiveForm({
|
|||||||
...initialValues,
|
...initialValues,
|
||||||
},
|
},
|
||||||
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
|
setSubmitting(true);
|
||||||
|
const entries = formik.values.entries.filter((item) => {
|
||||||
|
if (item.invoice_id !== undefined) {
|
||||||
|
return { ...item };
|
||||||
|
}
|
||||||
|
});
|
||||||
const form = {
|
const form = {
|
||||||
...values,
|
...values,
|
||||||
|
entries,
|
||||||
};
|
};
|
||||||
const savePaymentReceive = (mediaIds) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const requestForm = { ...form, media_ids: mediaIds };
|
|
||||||
|
|
||||||
requestSubmitPaymentReceive(requestForm)
|
const requestForm = { ...form };
|
||||||
.the((response) => {
|
|
||||||
AppToaster.show({
|
if (paymentReceive && paymentReceive.id) {
|
||||||
message: formatMessage({
|
requestEditPaymentReceive(paymentReceive.id, requestForm)
|
||||||
id: 'the_payment_receive_has_been_successfully_created',
|
.then((response) => {
|
||||||
}),
|
AppToaster.show({
|
||||||
intent: Intent.SUCCESS,
|
message: formatMessage({
|
||||||
});
|
id: 'the_payment_receive_has_been_successfully_edited',
|
||||||
setSubmitting(false);
|
}),
|
||||||
clearSavedMediaIds();
|
intent: Intent.SUCCESS,
|
||||||
savePaymentReceiveSubmit({ action: 'new', ...payload });
|
|
||||||
})
|
|
||||||
.catch((errors) => {
|
|
||||||
setSubmitting(false);
|
|
||||||
});
|
});
|
||||||
});
|
setSubmitting(false);
|
||||||
Promise.all([saveMedia(), deleteMedia()])
|
savePaymentReceiveSubmit({ action: 'update', ...payload });
|
||||||
.then(([savedMediaResponses]) => {
|
resetForm();
|
||||||
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
})
|
||||||
savedMediaIds.current = mediaIds;
|
.catch((error) => {
|
||||||
return savedMediaResponses;
|
setSubmitting(false);
|
||||||
})
|
});
|
||||||
.then(() => {
|
} else {
|
||||||
return savePaymentReceive(savedMediaIds.current);
|
requestSubmitPaymentReceive(requestForm)
|
||||||
});
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_receive_has_been_successfully_created',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
resetForm();
|
||||||
|
savePaymentReceiveSubmit({ action: 'new', ...payload });
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,33 +282,47 @@ function PaymentReceiveForm({
|
|||||||
formik.resetForm();
|
formik.resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClickAddNewRow = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...formik.values.entries, defaultPaymentReceive]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAllLines = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...repeatValue(defaultPaymentReceive, MIN_LINES_NUMBER)]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
console.log(formik.errors, 'ERROR');
|
||||||
return (
|
return (
|
||||||
<div className={'payment_receive_form'}>
|
<div className={'payment_receive_form'}>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<PaymentReceiveHeader formik={formik} />
|
<PaymentReceiveHeader formik={formik} />
|
||||||
<FormGroup
|
<PaymentReceiveItemsTable
|
||||||
label={<T id={'statement'} />}
|
entries={formik.values.entries}
|
||||||
className={'form-group--statement'}
|
customer_id={formik.values.customer_id}
|
||||||
>
|
onClickAddNewRow={handleClickAddNewRow}
|
||||||
<TextArea
|
onClickClearAllLines={handleClearAllLines}
|
||||||
growVertically={true}
|
formik={formik}
|
||||||
{...formik.getFieldProps('statement')}
|
invoices={paymentReceiveInvoices}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
{/* <Dragzone
|
||||||
|
|
||||||
<Dragzone
|
|
||||||
initialFiles={initialAttachmentFiles}
|
initialFiles={initialAttachmentFiles}
|
||||||
onDrop={handleDropFiles}
|
onDrop={handleDropFiles}
|
||||||
onDeleteFile={handleDeleteFile}
|
onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/> */}
|
||||||
<PaymentReceiveFooter
|
|
||||||
formik={formik}
|
|
||||||
onSubmit={handleSubmitClick}
|
|
||||||
onCancel={handleCancelClick}
|
|
||||||
onClearClick={handleClearClick}
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<PaymentReceiveFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
paymentReceive={paymentReceive}
|
||||||
|
onCancel={handleCancelClick}
|
||||||
|
onClearClick={handleClearClick}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -257,4 +331,8 @@ export default compose(
|
|||||||
withPaymentReceivesActions,
|
withPaymentReceivesActions,
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withMediaActions,
|
withMediaActions,
|
||||||
|
withPaymentReceives(({ paymentReceivesItems }) => ({
|
||||||
|
paymentReceivesItems,
|
||||||
|
})),
|
||||||
|
withPaymentReceiveDetail(),
|
||||||
)(PaymentReceiveForm);
|
)(PaymentReceiveForm);
|
||||||
|
|||||||
@@ -7,11 +7,23 @@ export default function PaymentReceiveFormFooter({
|
|||||||
onSubmitClick,
|
onSubmitClick,
|
||||||
onCancelClick,
|
onCancelClick,
|
||||||
onClearClick,
|
onClearClick,
|
||||||
|
paymentReceive,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form__floating-footer'}>
|
<div className={'estimate-form__floating-footer'}>
|
||||||
<Button disabled={isSubmitting} intent={Intent.PRIMARY} type="submit">
|
<Button
|
||||||
<T id={'save_send'} />
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ redirect: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{paymentReceive && paymentReceive.id ? (
|
||||||
|
<T id={'edit'} />
|
||||||
|
) : (
|
||||||
|
<T id={'save_send'} />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -20,6 +32,9 @@ export default function PaymentReceiveFormFooter({
|
|||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
name={'save'}
|
name={'save'}
|
||||||
type="submit"
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ redirect: false });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<T id={'save'} />
|
<T id={'save'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import {
|
|||||||
|
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@@ -106,8 +109,55 @@ function PaymentReceiveFormHeader({
|
|||||||
labelProp={'display_name'}
|
labelProp={'display_name'}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* Payment date */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'deposit_account'} />}
|
label={<T id={'payment_date'} />}
|
||||||
|
inline={true}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
intent={errors.payment_date && touched.payment_date && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DateInput
|
||||||
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
|
value={tansformDateValue(values.payment_date)}
|
||||||
|
onChange={handleDateChange('payment_date')}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* payment receive no */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_receive_no'} />}
|
||||||
|
inline={true}
|
||||||
|
className={('form-group--payment_receive_no', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={
|
||||||
|
errors.payment_receive_no &&
|
||||||
|
touched.payment_receive_no &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_receive_no" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={
|
||||||
|
errors.payment_receive_no &&
|
||||||
|
touched.payment_receive_no &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('payment_receive_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* deposit account */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'deposit_to'} />}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--deposit_account_id',
|
'form-group--deposit_account_id',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
@@ -129,53 +179,37 @@ function PaymentReceiveFormHeader({
|
|||||||
>
|
>
|
||||||
<AccountsSelectList
|
<AccountsSelectList
|
||||||
accounts={depositAccounts}
|
accounts={depositAccounts}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
onAccountSelected={onChangeSelect('deposit_account_id')}
|
onAccountSelected={onChangeSelect('deposit_account_id')}
|
||||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||||
selectedAccountId={values.deposit_account_id}
|
selectedAccountId={values.deposit_account_id}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
|
||||||
label={<T id={'payment_date'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
intent={errors.payment_date && touched.payment_date && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
|
||||||
value={tansformDateValue(values.payment_date)}
|
|
||||||
onChange={handleDateChange}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
</div>
|
||||||
{/* payment receive no */}
|
|
||||||
<FormGroup
|
{/* Receive amount */}
|
||||||
label={<T id={'payment_receive_no'} />}
|
|
||||||
|
{/* <FormGroup
|
||||||
|
label={<T id={'receive_amount'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={('form-group--payment_receive_no', Classes.FILL)}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
className={classNames('form-group--', Classes.FILL)}
|
||||||
intent={
|
intent={
|
||||||
errors.payment_receive_no &&
|
errors.receive_amount && touched.receive_amount && Intent.DANGER
|
||||||
touched.payment_receive_no &&
|
|
||||||
Intent.DANGER
|
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage name="payment_receive_no" {...{ errors, touched }} />
|
<ErrorMessage name="receive_amount" {...{ errors, touched }} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={
|
intent={
|
||||||
errors.payment_receive_no &&
|
errors.receive_amount && touched.receive_amount && Intent.DANGER
|
||||||
touched.payment_receive_no &&
|
|
||||||
Intent.DANGER
|
|
||||||
}
|
}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
{...getFieldProps('payment_receive_no')}
|
{...getFieldProps('receive_amount')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup> */}
|
||||||
|
|
||||||
{/* reference_no */}
|
{/* reference_no */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import DataTable from 'components/DataTable';
|
import { Icon, DataTable } from 'components';
|
||||||
import Icon from 'components/Icon';
|
import moment from 'moment';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { compose, formattedAmount, transformUpdatedRows } from 'utils';
|
import { compose, formattedAmount, transformUpdatedRows } from 'utils';
|
||||||
import {
|
import {
|
||||||
@@ -10,4 +12,257 @@ import {
|
|||||||
MoneyFieldCell,
|
MoneyFieldCell,
|
||||||
EstimatesListFieldCell,
|
EstimatesListFieldCell,
|
||||||
DivFieldCell,
|
DivFieldCell,
|
||||||
|
EmptyDiv,
|
||||||
} from 'components/DataTableCells';
|
} from 'components/DataTableCells';
|
||||||
|
import withInvoices from '../Invoice/withInvoices';
|
||||||
|
import withInvoiceActions from '../Invoice/withInvoiceActions';
|
||||||
|
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import { omit, pick } from 'lodash';
|
||||||
|
|
||||||
|
const ActionsCellRenderer = ({
|
||||||
|
row: { index },
|
||||||
|
column: { id },
|
||||||
|
cell: { value },
|
||||||
|
data,
|
||||||
|
payload,
|
||||||
|
}) => {
|
||||||
|
if (data.length <= index + 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const onRemoveRole = () => {
|
||||||
|
payload.removeRow(index);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
|
||||||
|
<Button
|
||||||
|
icon={<Icon icon={'times-circle'} iconSize={14} />}
|
||||||
|
iconSize={14}
|
||||||
|
className="m12"
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={onRemoveRole}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CellRenderer = (content, type) => (props) => {
|
||||||
|
if (props.data.length === props.row.index + 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return content(props);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TotalCellRederer = (content, type) => (props) => {
|
||||||
|
if (props.data.length === props.row.index + 1) {
|
||||||
|
const total = props.data.reduce((total, entry) => {
|
||||||
|
const amount = parseInt(entry[type], 10);
|
||||||
|
const computed = amount ? total + amount : total;
|
||||||
|
|
||||||
|
return computed;
|
||||||
|
}, 0);
|
||||||
|
return <span>{formattedAmount(total, 'USD')}</span>;
|
||||||
|
}
|
||||||
|
return content(props);
|
||||||
|
};
|
||||||
|
|
||||||
|
function PaymentReceiveItemsTable({
|
||||||
|
//#ownProps
|
||||||
|
onClickRemoveRow,
|
||||||
|
onClickAddNewRow,
|
||||||
|
onClickClearAllLines,
|
||||||
|
entries,
|
||||||
|
formik: { errors, setFieldValue, values },
|
||||||
|
|
||||||
|
dueInvoices,
|
||||||
|
customer_id,
|
||||||
|
invoices,
|
||||||
|
}) {
|
||||||
|
const [rows, setRows] = useState([]);
|
||||||
|
const [entrie, setEntrie] = useState([]);
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRows([...dueInvoices.map((e) => ({ ...e })), ...invoices, {}]);
|
||||||
|
setEntrie([
|
||||||
|
...dueInvoices.map((e) => {
|
||||||
|
return { id: e.id, payment_amount: e.payment_amount };
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}, [dueInvoices]);
|
||||||
|
|
||||||
|
console.log(rows, 'rows');
|
||||||
|
console.log(entrie, 'entrie');
|
||||||
|
console.log(values, 'values');
|
||||||
|
// useEffect(() => {
|
||||||
|
// setRows([...dueInvoices.map((e) => ({ ...e })), {}]);
|
||||||
|
|
||||||
|
// setEntrie([
|
||||||
|
// ...dueInvoices.map((e) => {
|
||||||
|
// return { id: e.id, payment_amount: e.payment_amount };
|
||||||
|
// }),
|
||||||
|
// ]);
|
||||||
|
// }, [dueInvoices]);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: '#',
|
||||||
|
accessor: 'index',
|
||||||
|
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
|
||||||
|
width: 40,
|
||||||
|
disableResizing: true,
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'Date' }),
|
||||||
|
id: 'invoice_date',
|
||||||
|
accessor: (r) => moment(r.invoice_date).format('YYYY MMM DD'),
|
||||||
|
Cell: CellRenderer(EmptyDiv, 'invoice_date'),
|
||||||
|
disableSortBy: true,
|
||||||
|
disableResizing: true,
|
||||||
|
width: 250,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'invocie_number' }),
|
||||||
|
accessor: (row) => `#${row.invoice_no}`,
|
||||||
|
Cell: CellRenderer(EmptyDiv, 'invoice_no'),
|
||||||
|
disableSortBy: true,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'invoice_amount' }),
|
||||||
|
accessor: 'balance',
|
||||||
|
Cell: CellRenderer(DivFieldCell, 'balance'),
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 100,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'amount_due' }),
|
||||||
|
accessor: 'due_amount',
|
||||||
|
Cell: TotalCellRederer(DivFieldCell, 'due_amount'),
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 150,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: formatMessage({ id: 'payment_amount' }),
|
||||||
|
accessor: 'payment_amount',
|
||||||
|
Cell: TotalCellRederer(MoneyFieldCell, 'payment_amount'),
|
||||||
|
|
||||||
|
disableSortBy: true,
|
||||||
|
width: 150,
|
||||||
|
className: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: '',
|
||||||
|
accessor: 'action',
|
||||||
|
Cell: ActionsCellRenderer,
|
||||||
|
className: 'actions',
|
||||||
|
disableSortBy: true,
|
||||||
|
disableResizing: true,
|
||||||
|
width: 45,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemoveRow = useCallback(
|
||||||
|
(rowIndex) => {
|
||||||
|
if (rows.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const removeIndex = parseInt(rowIndex, 10);
|
||||||
|
const newRows = rows.filter((row, index) => index !== removeIndex);
|
||||||
|
|
||||||
|
setFieldValue(
|
||||||
|
'entries',
|
||||||
|
newRows.map((row, index) => ({
|
||||||
|
...omit(row),
|
||||||
|
index: index - 1,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
onClickRemoveRow && onClickRemoveRow(removeIndex);
|
||||||
|
},
|
||||||
|
[entrie, setFieldValue, onClickRemoveRow],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClickNewRow = () => {
|
||||||
|
onClickAddNewRow && onClickAddNewRow();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickClearAllLines = () => {
|
||||||
|
onClickClearAllLines && onClickClearAllLines();
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowClassNames = useCallback((row) => {
|
||||||
|
return { 'row--total': rows.length === row.index + 1 };
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleUpdateData = useCallback(
|
||||||
|
(rowIndex, columnId, value) => {
|
||||||
|
const newRows = rows.map((row, index) => {
|
||||||
|
if (index === rowIndex) {
|
||||||
|
return {
|
||||||
|
...rows[rowIndex],
|
||||||
|
[columnId]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
setRows(newRows);
|
||||||
|
// setEntrie(newRows);
|
||||||
|
setFieldValue(
|
||||||
|
'entries',
|
||||||
|
newRows.map((row) => ({
|
||||||
|
...pick(row, ['payment_amount']),
|
||||||
|
invoice_id: row.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[rows, setFieldValue, setEntrie,setRows],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'estimate-form__table'}>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={rows}
|
||||||
|
rowClassNames={rowClassNames}
|
||||||
|
spinnerProps={false}
|
||||||
|
payload={{
|
||||||
|
errors: errors.entries || [],
|
||||||
|
updateData: handleUpdateData,
|
||||||
|
removeRow: handleRemoveRow,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={'mt1'}>
|
||||||
|
<Button
|
||||||
|
small={true}
|
||||||
|
className={'button--secondary button--new-line'}
|
||||||
|
onClick={onClickNewRow}
|
||||||
|
>
|
||||||
|
<T id={'new_lines'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
small={true}
|
||||||
|
className={'button--secondary button--clear-lines ml1'}
|
||||||
|
onClick={handleClickClearAllLines}
|
||||||
|
>
|
||||||
|
<T id={'clear_all_lines'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withInvoices(({ dueInvoices }) => ({
|
||||||
|
dueInvoices,
|
||||||
|
})),
|
||||||
|
)(PaymentReceiveItemsTable);
|
||||||
|
|||||||
182
client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js
Normal file
182
client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import React, { useEffect, useCallback, useMemo, useState } from 'react';
|
||||||
|
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery, queryCache } from 'react-query';
|
||||||
|
import { Alert, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import PaymentReceivesDataTable from './PaymentReceivesDataTable';
|
||||||
|
import PaymentReceiveActionsBar from './PaymentReceiveActionsBar';
|
||||||
|
import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
|
import withPaymentReceives from './withPaymentReceives';
|
||||||
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentReceiveList({
|
||||||
|
// #withDashboardActions
|
||||||
|
changePageTitle,
|
||||||
|
|
||||||
|
// #withViewsActions
|
||||||
|
requestFetchResourceViews,
|
||||||
|
requestFetchResourceFields,
|
||||||
|
|
||||||
|
//#withPaymentReceives
|
||||||
|
paymentReceivesTableQuery,
|
||||||
|
|
||||||
|
//#withPaymentReceivesActions
|
||||||
|
requestFetchPaymentReceiveTable,
|
||||||
|
requestDeletePaymentReceive,
|
||||||
|
addPaymentReceivesTableQueries,
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [deletePaymentReceive, setDeletePaymentReceive] = useState(false);
|
||||||
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changePageTitle(formatMessage({ id: 'payment_Receive_list' }));
|
||||||
|
}, [changePageTitle, formatMessage]);
|
||||||
|
|
||||||
|
const fetchResourceViews = useQuery(
|
||||||
|
['resource-views', 'payment_receives'],
|
||||||
|
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchResourceFields = useQuery(
|
||||||
|
['resource-fields', 'payment_receives'],
|
||||||
|
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchPaymentReceives = useQuery(
|
||||||
|
['paymantReceives-table', paymentReceivesTableQuery],
|
||||||
|
() => requestFetchPaymentReceiveTable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
//handle dalete Payment Receive
|
||||||
|
const handleDeletePaymentReceive = useCallback(
|
||||||
|
(paymentReceive) => {
|
||||||
|
setDeletePaymentReceive(paymentReceive);
|
||||||
|
},
|
||||||
|
[setDeletePaymentReceive],
|
||||||
|
);
|
||||||
|
|
||||||
|
// handle cancel Payment Receive
|
||||||
|
const handleCancelPaymentReceiveDelete = useCallback(() => {
|
||||||
|
setDeletePaymentReceive(false);
|
||||||
|
}, [setDeletePaymentReceive]);
|
||||||
|
|
||||||
|
// handleConfirm delete payment receive
|
||||||
|
const handleConfirmPaymentReceiveDelete = useCallback(() => {
|
||||||
|
requestDeletePaymentReceive(deletePaymentReceive.id).then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_receive_has_been_successfully_deleted',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setDeletePaymentReceive(false);
|
||||||
|
});
|
||||||
|
}, [deletePaymentReceive, requestDeletePaymentReceive, formatMessage]);
|
||||||
|
|
||||||
|
const handleEditPaymentReceive = useCallback((payment) => {
|
||||||
|
history.push(`/payment-receive/${payment.id}/edit`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculates the selected rows count.
|
||||||
|
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
|
||||||
|
selectedRows,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleFetchData = useCallback(
|
||||||
|
({ pageIndex, pageSize, sortBy }) => {
|
||||||
|
const page = pageIndex + 1;
|
||||||
|
|
||||||
|
addPaymentReceivesTableQueries({
|
||||||
|
...(sortBy.length > 0
|
||||||
|
? {
|
||||||
|
column_sort_by: sortBy[0].id,
|
||||||
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
page_size: pageSize,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addPaymentReceivesTableQueries],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle filter change to re-fetch data-table.
|
||||||
|
const handleFilterChanged = useCallback(() => {}, [fetchPaymentReceives]);
|
||||||
|
|
||||||
|
// Handle selected rows change.
|
||||||
|
const handleSelectedRowsChange = useCallback(
|
||||||
|
(_payment) => {
|
||||||
|
setSelectedRows(_payment);
|
||||||
|
},
|
||||||
|
[setSelectedRows],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
// loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
|
||||||
|
name={'payment_receives'}
|
||||||
|
>
|
||||||
|
<PaymentReceiveActionsBar
|
||||||
|
// onBulkDelete={}
|
||||||
|
selectedRows={selectedRows}
|
||||||
|
onFilterChanged={handleFilterChanged}
|
||||||
|
/>
|
||||||
|
<DashboardPageContent>
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
exact={true}
|
||||||
|
path={[
|
||||||
|
'/payment-receives/:custom_view_id/custom_view',
|
||||||
|
'/payment-receives',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<PaymentReceiveViewTabs />
|
||||||
|
<PaymentReceivesDataTable
|
||||||
|
loading={fetchPaymentReceives.isFetching}
|
||||||
|
onDeletePaymentReceive={handleDeletePaymentReceive}
|
||||||
|
onFetchData={handleFetchData}
|
||||||
|
onEditPaymentReceive={handleEditPaymentReceive}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
<Alert
|
||||||
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
|
confirmButtonText={<T id={'delete'} />}
|
||||||
|
icon={'trash'}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
isOpen={deletePaymentReceive}
|
||||||
|
onCancel={handleCancelPaymentReceiveDelete}
|
||||||
|
onConfirm={handleConfirmPaymentReceiveDelete}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<T id={'once_delete_this_payment_receive_you_will_able_to_restore_it'} />
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
|
</DashboardPageContent>
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withResourceActions,
|
||||||
|
withPaymentReceivesActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withViewsActions,
|
||||||
|
withPaymentReceives(({ paymentReceivesTableQuery }) => ({
|
||||||
|
paymentReceivesTableQuery,
|
||||||
|
})),
|
||||||
|
)(PaymentReceiveList);
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
|
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||||
|
import { useParams, withRouter } from 'react-router-dom';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { pick, debounce } from 'lodash';
|
||||||
|
|
||||||
|
import { DashboardViewsTabs } from 'components';
|
||||||
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import withPaymentReceives from './withPaymentReceives';
|
||||||
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withViewDetails from 'containers/Views/withViewDetails';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentReceiveViewTabs({
|
||||||
|
//#withPaymentReceives
|
||||||
|
paymentReceivesViews,
|
||||||
|
|
||||||
|
//#withPaymentReceivesActions
|
||||||
|
changePaymentReceiveView,
|
||||||
|
addPaymentReceivesTableQueries,
|
||||||
|
|
||||||
|
// #withViewDetails
|
||||||
|
viewItem,
|
||||||
|
|
||||||
|
// #withDashboardActions
|
||||||
|
setTopbarEditView,
|
||||||
|
changePageSubtitle,
|
||||||
|
|
||||||
|
//#Own Props
|
||||||
|
customViewChanged,
|
||||||
|
onViewChanged,
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
const { custom_view_id: customViewId = null } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changePaymentReceiveView(customViewId || -1);
|
||||||
|
setTopbarEditView(customViewId);
|
||||||
|
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||||
|
|
||||||
|
addPaymentReceivesTableQueries({
|
||||||
|
custom_view_id: customViewId,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
setTopbarEditView(null);
|
||||||
|
changePageSubtitle('');
|
||||||
|
changePaymentReceiveView(null);
|
||||||
|
};
|
||||||
|
}, [customViewId, addPaymentReceivesTableQueries, changePaymentReceiveView]);
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
onViewChanged && onViewChanged(customViewId);
|
||||||
|
}, [customViewId]);
|
||||||
|
|
||||||
|
const debounceChangeHistory = useRef(
|
||||||
|
debounce((toUrl) => {
|
||||||
|
history.push(toUrl);
|
||||||
|
}, 250),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTabsChange = (viewId) => {
|
||||||
|
const toPath = viewId ? `${viewId}/custom_view` : '';
|
||||||
|
debounceChangeHistory.current(`/payment-receives/${toPath}`);
|
||||||
|
setTopbarEditView(viewId);
|
||||||
|
};
|
||||||
|
const tabs = paymentReceivesViews.map((view) => ({
|
||||||
|
...pick(view, ['name', 'id']),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Handle click a new view tab.
|
||||||
|
const handleClickNewView = () => {
|
||||||
|
setTopbarEditView(null);
|
||||||
|
history.push('/custom_views/payment-receives/new');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Navbar className={'navbar--dashboard-views'}>
|
||||||
|
<NavbarGroup align={Alignment.LEFT}>
|
||||||
|
<DashboardViewsTabs
|
||||||
|
initialViewId={customViewId}
|
||||||
|
baseUrl={'/payment-receives'}
|
||||||
|
tabs={tabs}
|
||||||
|
onNewViewTabClick={handleClickNewView}
|
||||||
|
onChange={handleTabsChange}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</Navbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
viewId: ownProps.match.params.custom_view_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withPaymentReceivesViewTabs = connect(mapStateToProps);
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
withPaymentReceivesViewTabs,
|
||||||
|
withPaymentReceivesActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withViewDetails(),
|
||||||
|
withPaymentReceives(({ paymentReceivesViews }) => ({
|
||||||
|
paymentReceivesViews,
|
||||||
|
})),
|
||||||
|
)(PaymentReceiveViewTabs);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useState, useEffect, useMemo } from 'react';
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|||||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
//#withInvoiceActions
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withInvoiceActions from '../Invoice/withInvoiceActions';
|
||||||
|
import withInvoices from '../Invoice/withInvoices';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -22,9 +24,16 @@ function PaymentReceives({
|
|||||||
//#withItemsActions
|
//#withItemsActions
|
||||||
requestFetchItems,
|
requestFetchItems,
|
||||||
|
|
||||||
//#withInvoiceActions
|
//#withPaymentReceivesActions
|
||||||
|
requestFetchPaymentReceive,
|
||||||
|
|
||||||
|
//#withInvoicesActions
|
||||||
|
requestFetchDueInvoices,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { id } = useParams();
|
||||||
|
const [customerId, setCustomerId] = useState(null);
|
||||||
|
const [payload, setPayload] = useState(false);
|
||||||
|
|
||||||
// Handle fetch accounts data
|
// Handle fetch accounts data
|
||||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
@@ -32,30 +41,59 @@ function PaymentReceives({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Handle fetch Items data table or list
|
// Handle fetch Items data table or list
|
||||||
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
const fetchItems = useQuery('items-list', () => requestFetchItems({}));
|
||||||
|
|
||||||
// Handle fetch customers data table or list
|
// Handle fetch customers data table or list
|
||||||
const fetchCustomers = useQuery('customers-table', () =>
|
const fetchCustomers = useQuery('customers-list', () =>
|
||||||
requestFetchCustomers({}),
|
requestFetchCustomers({}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
const fetchPaymentReceive = useQuery(
|
||||||
|
['payment-receive', id],
|
||||||
|
(key, _id) => requestFetchPaymentReceive(_id),
|
||||||
|
{ enabled: !!id },
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchDueInvoices = useQuery(
|
||||||
|
['due-invoies', customerId],
|
||||||
|
(key, query) => requestFetchDueInvoices(query),
|
||||||
|
{ enabled: !!customerId },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFormSubmit = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
payload.redirect && history.push('/payment-receives');
|
||||||
|
},
|
||||||
|
[history],
|
||||||
|
);
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
|
const handleCustomerChange = (customerId) => {
|
||||||
|
if (id) {
|
||||||
|
setCustomerId(!customerId);
|
||||||
|
} else {
|
||||||
|
setCustomerId(customerId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
loading={
|
loading={
|
||||||
fetchCustomers.isFetching ||
|
fetchCustomers.isFetching ||
|
||||||
fetchItems.isFetching ||
|
fetchItems.isFetching ||
|
||||||
fetchAccounts.isFetching
|
fetchAccounts.isFetching ||
|
||||||
|
fetchPaymentReceive.isFetching
|
||||||
}
|
}
|
||||||
|
name={'payment-receive'}
|
||||||
>
|
>
|
||||||
<PaymentReceiveForm
|
<PaymentReceiveForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
|
paymentReceiveId={id}
|
||||||
|
paymentReceiveInvoices={id}
|
||||||
onCancelForm={handleCancel}
|
onCancelForm={handleCancel}
|
||||||
|
onCustomerChange={handleCustomerChange}
|
||||||
/>
|
/>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
@@ -65,5 +103,6 @@ export default compose(
|
|||||||
withCustomersActions,
|
withCustomersActions,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
withAccountsActions,
|
withAccountsActions,
|
||||||
// withInvoiceActions
|
withPaymentReceivesActions,
|
||||||
|
withInvoiceActions,
|
||||||
)(PaymentReceives);
|
)(PaymentReceives);
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Intent,
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Popover,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
MenuDivider,
|
||||||
|
Position,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { withRouter } from 'react-router';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
|
import { DataTable, Money, Icon } from 'components';
|
||||||
|
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withViewDetails from 'containers/Views/withViewDetails';
|
||||||
|
|
||||||
|
import withPaymentReceives from './withPaymentReceives';
|
||||||
|
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||||
|
import withCurrentView from 'containers/Views/withCurrentView';
|
||||||
|
|
||||||
|
function PaymentReceivesDataTable({
|
||||||
|
//#withPaymentReceives
|
||||||
|
PaymentReceivesCurrentPage,
|
||||||
|
paymentReceivesPageination,
|
||||||
|
paymentReceivesLoading,
|
||||||
|
paymentReceivesItems,
|
||||||
|
|
||||||
|
// #withDashboardActions
|
||||||
|
changeCurrentView,
|
||||||
|
changePageSubtitle,
|
||||||
|
setTopbarEditView,
|
||||||
|
|
||||||
|
// #withView
|
||||||
|
viewMeta,
|
||||||
|
|
||||||
|
//#OwnProps
|
||||||
|
loading,
|
||||||
|
onFetchData,
|
||||||
|
onEditPaymentReceive,
|
||||||
|
onDeletePaymentReceive,
|
||||||
|
onSelectedRowsChange,
|
||||||
|
}) {
|
||||||
|
const [initialMount, setInitialMount] = useState(false);
|
||||||
|
const { custom_view_id: customViewId } = useParams();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialMount(false);
|
||||||
|
}, [customViewId]);
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
if (!paymentReceivesLoading) {
|
||||||
|
setInitialMount(true);
|
||||||
|
}
|
||||||
|
}, [paymentReceivesLoading, setInitialMount]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (customViewId) {
|
||||||
|
// changeCurrentView(customViewId);
|
||||||
|
// setTopbarEditView(customViewId);
|
||||||
|
// }
|
||||||
|
// changePageSubtitle(customViewId && viewMeta ? viewMeta.name : '');
|
||||||
|
// }, [
|
||||||
|
// customViewId,
|
||||||
|
// changeCurrentView,
|
||||||
|
// changePageSubtitle,
|
||||||
|
// setTopbarEditView,
|
||||||
|
// viewMeta,
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
const handleEditPaymentReceive = useCallback(
|
||||||
|
(paymentReceive) => () => {
|
||||||
|
onEditPaymentReceive && onEditPaymentReceive(paymentReceive);
|
||||||
|
},
|
||||||
|
[onEditPaymentReceive],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDeletePaymentReceive = useCallback(
|
||||||
|
(paymentReceive) => () => {
|
||||||
|
onDeletePaymentReceive && onDeletePaymentReceive(paymentReceive);
|
||||||
|
},
|
||||||
|
[onDeletePaymentReceive],
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionMenuList = useCallback(
|
||||||
|
(paymentReceive) => (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem text={formatMessage({ id: 'view_details' })} />
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem
|
||||||
|
text={formatMessage({ id: 'edit_payment_receive' })}
|
||||||
|
onClick={handleEditPaymentReceive(paymentReceive)}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
text={formatMessage({ id: 'delete_payment_receive' })}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={handleDeletePaymentReceive(paymentReceive)}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
),
|
||||||
|
[handleDeletePaymentReceive, handleEditPaymentReceive, formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onRowContextMenu = useCallback(
|
||||||
|
(cell) => {
|
||||||
|
return actionMenuList(cell.row.original);
|
||||||
|
},
|
||||||
|
[actionMenuList],
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'payment_date',
|
||||||
|
Header: formatMessage({ id: 'payment_date' }),
|
||||||
|
accessor: (r) => moment(r.payment_date).format('YYYY MMM DD'),
|
||||||
|
width: 140,
|
||||||
|
className: 'payment_date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'customer_id',
|
||||||
|
Header: formatMessage({ id: 'customer_name' }),
|
||||||
|
accessor: 'customer.display_name',
|
||||||
|
width: 140,
|
||||||
|
className: 'customer_id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'payment_receive_no',
|
||||||
|
Header: formatMessage({ id: 'payment_receive_no' }),
|
||||||
|
accessor: (row) => `#${row.payment_receive_no}`,
|
||||||
|
width: 140,
|
||||||
|
className: 'payment_receive_no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'amount',
|
||||||
|
Header: formatMessage({ id: 'amount' }),
|
||||||
|
accessor: (r) => <Money amount={r.amount} currency={'USD'} />,
|
||||||
|
width: 140,
|
||||||
|
className: 'amount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'reference_no',
|
||||||
|
Header: formatMessage({ id: 'reference_no' }),
|
||||||
|
accessor: 'reference_no',
|
||||||
|
width: 140,
|
||||||
|
className: 'reference_no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deposit_account_id',
|
||||||
|
Header: formatMessage({ id: 'deposit_account' }),
|
||||||
|
accessor: 'deposit_account.name',
|
||||||
|
width: 140,
|
||||||
|
className: 'deposit_account_id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
Header: '',
|
||||||
|
Cell: ({ cell }) => (
|
||||||
|
<Popover
|
||||||
|
content={actionMenuList(cell.row.original)}
|
||||||
|
position={Position.RIGHT_BOTTOM}
|
||||||
|
>
|
||||||
|
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||||
|
</Popover>
|
||||||
|
),
|
||||||
|
className: 'actions',
|
||||||
|
width: 50,
|
||||||
|
disableResizing: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[actionMenuList, formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDataTableFetchData = useCallback(
|
||||||
|
(...args) => {
|
||||||
|
onFetchData && onFetchData(...args);
|
||||||
|
},
|
||||||
|
[onFetchData],
|
||||||
|
);
|
||||||
|
const handleSelectedRowsChange = useCallback(
|
||||||
|
(selectedRows) => {
|
||||||
|
onSelectedRowsChange &&
|
||||||
|
onSelectedRowsChange(selectedRows.map((s) => s.original));
|
||||||
|
},
|
||||||
|
[onSelectedRowsChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={PaymentReceivesCurrentPage}
|
||||||
|
onFetchData={handleDataTableFetchData}
|
||||||
|
manualSortBy={true}
|
||||||
|
selectionColumn={true}
|
||||||
|
noInitialFetch={true}
|
||||||
|
sticky={true}
|
||||||
|
loading={paymentReceivesLoading && !initialMount}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
rowContextMenu={onRowContextMenu}
|
||||||
|
pagination={true}
|
||||||
|
pagesCount={paymentReceivesPageination.pagesCount}
|
||||||
|
initialPageSize={paymentReceivesPageination.pageSize}
|
||||||
|
initialPageIndex={paymentReceivesPageination.page - 1}
|
||||||
|
/>
|
||||||
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
withCurrentView,
|
||||||
|
withDialogActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withPaymentReceivesActions,
|
||||||
|
withPaymentReceives(
|
||||||
|
({
|
||||||
|
PaymentReceivesCurrentPage,
|
||||||
|
paymentReceivesLoading,
|
||||||
|
paymentReceivesPageination,
|
||||||
|
}) => ({
|
||||||
|
PaymentReceivesCurrentPage,
|
||||||
|
paymentReceivesLoading,
|
||||||
|
paymentReceivesPageination,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
withViewDetails(),
|
||||||
|
)(PaymentReceivesDataTable);
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
getPaymentReceiveByIdFactory,
|
||||||
|
getPaymentReceiveInvoices,
|
||||||
|
} from 'store/PaymentReceive/paymentReceive.selector';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const getPaymentReceiveById = getPaymentReceiveByIdFactory();
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
paymentReceive: getPaymentReceiveById(state, props),
|
||||||
|
paymentReceiveInvoices: getPaymentReceiveInvoices(state, props),
|
||||||
|
});
|
||||||
|
return connect(mapStateToProps);
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
|
import {
|
||||||
|
getPaymentReceiveCurrentPageFactory,
|
||||||
|
getPaymentReceivePaginationMetaFactory,
|
||||||
|
getPaymentReceiveTableQuery,
|
||||||
|
} from 'store/PaymentReceive/paymentReceive.selector';
|
||||||
|
|
||||||
|
|
||||||
|
export default (mapState) => {
|
||||||
|
const getPyamentReceivesItems = getPaymentReceiveCurrentPageFactory();
|
||||||
|
const getPyamentReceivesPaginationMeta = getPaymentReceivePaginationMetaFactory();
|
||||||
|
const mapStateToProps = (state, props) => {
|
||||||
|
const query = getPaymentReceiveTableQuery(state, props);
|
||||||
|
const mapped = {
|
||||||
|
PaymentReceivesCurrentPage: getPyamentReceivesItems(state, props, query),
|
||||||
|
paymentReceivesViews: getResourceViews(state, props, 'payment_receives'),
|
||||||
|
paymentReceivesItems: state.paymentReceives.items,
|
||||||
|
paymentReceivesTableQuery: query,
|
||||||
|
paymentReceivesPageination: getPyamentReceivesPaginationMeta(state, props, query),
|
||||||
|
paymentReceivesLoading: state.paymentReceives.loading,
|
||||||
|
};
|
||||||
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
|
};
|
||||||
|
return connect(mapStateToProps);
|
||||||
|
};
|
||||||
@@ -13,7 +13,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
dispatch(submitPaymentReceive({ form })),
|
dispatch(submitPaymentReceive({ form })),
|
||||||
requestFetchPaymentReceive: (id) => dispatch(fetchPaymentReceive({ id })),
|
requestFetchPaymentReceive: (id) => dispatch(fetchPaymentReceive({ id })),
|
||||||
requestEditPaymentReceive: (id, form) =>
|
requestEditPaymentReceive: (id, form) =>
|
||||||
dispatch(editPaymentReceive({ id, form })),
|
dispatch(editPaymentReceive( id, form )),
|
||||||
requestDeletePaymentReceive: (id) => dispatch(deletePaymentReceive({ id })),
|
requestDeletePaymentReceive: (id) => dispatch(deletePaymentReceive({ id })),
|
||||||
requestFetchPaymentReceiveTable: (query = {}) =>
|
requestFetchPaymentReceiveTable: (query = {}) =>
|
||||||
dispatch(fetchPaymentReceivesTable({ query: { ...query } })),
|
dispatch(fetchPaymentReceivesTable({ query: { ...query } })),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import moment from 'moment';
|
|||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
|
||||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
@@ -214,7 +215,7 @@ function ReceiptForm({
|
|||||||
const requestForm = { ...form };
|
const requestForm = { ...form };
|
||||||
|
|
||||||
if (receipt && receipt.id) {
|
if (receipt && receipt.id) {
|
||||||
requestEditReceipt(receipt.id && requestForm).then(() => {
|
requestEditReceipt(receipt.id, requestForm).then(() => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage({
|
||||||
id: 'the_receipt_has_been_successfully_edited',
|
id: 'the_receipt_has_been_successfully_edited',
|
||||||
@@ -244,6 +245,7 @@ function ReceiptForm({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
console.log(formik.errors, 'ERROR');
|
||||||
|
|
||||||
const handleDeleteFile = useCallback(
|
const handleDeleteFile = useCallback(
|
||||||
(_deletedFiles) => {
|
(_deletedFiles) => {
|
||||||
@@ -296,31 +298,38 @@ function ReceiptForm({
|
|||||||
onClickClearAllLines={handleClearAllLines}
|
onClickClearAllLines={handleClearAllLines}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
|
||||||
label={<T id={'receipt_message'} />}
|
|
||||||
className={'form-group--'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('receipt_message')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'statement'} />}
|
|
||||||
className={'form-group--statement'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('statement')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<Dragzone
|
<Row>
|
||||||
initialFiles={initialAttachmentFiles}
|
<Col>
|
||||||
onDrop={handleDropFiles}
|
<FormGroup
|
||||||
onDeleteFile={handleDeleteFile}
|
label={<T id={'receipt_message'} />}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
className={'form-group--'}
|
||||||
/>
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('receipt_message')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'statement'} />}
|
||||||
|
className={'form-group--statement'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('statement')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</form>
|
</form>
|
||||||
<ReceiptFormFooter
|
<ReceiptFormFooter
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default function ReceiptFormFooter({
|
|||||||
receipt,
|
receipt,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form__floating-footer'}>
|
<div className={'form__floating-footer'}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
|
|||||||
@@ -83,13 +83,17 @@ function ReceiptFormHeader({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div class="page-form receipt-form">
|
||||||
<div>
|
<div class="page-form__primary-section">
|
||||||
{/* customer name */}
|
{/*- Customer name -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames(
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
'form-group--customer',
|
||||||
|
)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
@@ -110,10 +114,11 @@ function ReceiptFormHeader({
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
{/*- Deposit account -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'deposit_account'} />}
|
label={<T id={'deposit_account'} />}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--deposit_account_id',
|
'form-group--deposit-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL,
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
@@ -172,6 +177,7 @@ function ReceiptFormHeader({
|
|||||||
/>
|
/>
|
||||||
</FormGroup> */}
|
</FormGroup> */}
|
||||||
|
|
||||||
|
{/*- Reference -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
@@ -185,6 +191,8 @@ function ReceiptFormHeader({
|
|||||||
{...getFieldProps('reference_no')}
|
{...getFieldProps('reference_no')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
{/*- Send to email -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'send_to_email'} />}
|
label={<T id={'send_to_email'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ function Receipts({
|
|||||||
requestFetchItems,
|
requestFetchItems,
|
||||||
|
|
||||||
//#withReceiptsActions
|
//#withReceiptsActions
|
||||||
requsetFetchInvoice,
|
requestFetchReceipt,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
const fetchReceipt = useQuery(
|
const fetchReceipt = useQuery(
|
||||||
['receipt', id],
|
['receipt', id],
|
||||||
(key, _id) => requsetFetchInvoice(_id),
|
(key, _id) => requestFetchReceipt(_id),
|
||||||
{ enabled: !!id },
|
{ enabled: !!id },
|
||||||
);
|
);
|
||||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
@@ -60,7 +60,7 @@ function Receipts({
|
|||||||
loading={
|
loading={
|
||||||
fetchCustomers.isFetching ||
|
fetchCustomers.isFetching ||
|
||||||
fetchItems.isFetching ||
|
fetchItems.isFetching ||
|
||||||
fetchAccounts.isFetching||
|
fetchAccounts.isFetching ||
|
||||||
fetchReceipt.isFetching
|
fetchReceipt.isFetching
|
||||||
}
|
}
|
||||||
name={'receipt-form'}
|
name={'receipt-form'}
|
||||||
|
|||||||
@@ -2,23 +2,25 @@ import { connect } from 'react-redux';
|
|||||||
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
import {
|
import {
|
||||||
getReceiptCurrentPageFactory,
|
getReceiptCurrentPageFactory,
|
||||||
getReceiptsTableQuery,
|
getReceiptsTableQueryFactory,
|
||||||
getReceiptsPaginationMetaFactory,
|
getReceiptsPaginationMetaFactory,
|
||||||
} from 'store/receipt/receipt.selector';
|
} from 'store/receipt/receipt.selector';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getReceiptsItems = getReceiptCurrentPageFactory();
|
const getReceiptsItems = getReceiptCurrentPageFactory();
|
||||||
const getReceiptPaginationMeta = getReceiptsPaginationMetaFactory();
|
const getReceiptPaginationMeta = getReceiptsPaginationMetaFactory();
|
||||||
|
const getReceiptsTableQuery = getReceiptsTableQueryFactory();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const query = getReceiptsTableQuery(state, props);
|
const tableQuery = getReceiptsTableQuery(state, props);
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
receiptsCurrentPage: getReceiptsItems(state, props, query),
|
receiptsCurrentPage: getReceiptsItems(state, props, tableQuery),
|
||||||
receiptview:getResourceViews(state, props, 'sales_receipts'),
|
receiptview:getResourceViews(state, props, 'sales_receipts'),
|
||||||
receiptItems: state.sales_receipts.items,
|
receiptItems: state.salesReceipts.items,
|
||||||
receiptTableQuery: query,
|
receiptTableQuery: tableQuery,
|
||||||
receiptsPagination: getReceiptPaginationMeta(state, props, query),
|
receiptsPagination: getReceiptPaginationMeta(state, props, tableQuery),
|
||||||
receiptsLoading: state.sales_receipts.loading,
|
receiptsLoading: state.salesReceipts.loading,
|
||||||
};
|
};
|
||||||
|
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const mapDipatchToProps = (dispatch) => ({
|
|||||||
requestEditVendor: (id, form) => dispatch(editVendor(id, form)),
|
requestEditVendor: (id, form) => dispatch(editVendor(id, form)),
|
||||||
requestFetchVendorsTable: (query = {}) =>
|
requestFetchVendorsTable: (query = {}) =>
|
||||||
dispatch(fetchVendorsTable({ query: { ...query } })),
|
dispatch(fetchVendorsTable({ query: { ...query } })),
|
||||||
requestDeleteEstimate: (id) => dispatch(deleteVendor({ id })),
|
requestDeleteVender: (id) => dispatch(deleteVendor({ id })),
|
||||||
|
|
||||||
changeVendorView: (id) =>
|
changeVendorView: (id) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
@@ -393,6 +393,9 @@ export default {
|
|||||||
base_currency_: 'Base currency',
|
base_currency_: 'Base currency',
|
||||||
date_format_: 'Date format',
|
date_format_: 'Date format',
|
||||||
category_name_: 'Category name',
|
category_name_: 'Category name',
|
||||||
|
sell_account_: 'Sell account',
|
||||||
|
cost_account_: 'Cost account',
|
||||||
|
inventory_account_: 'Inventory account',
|
||||||
view_name_: 'View name',
|
view_name_: 'View name',
|
||||||
time_zone: 'Time zone',
|
time_zone: 'Time zone',
|
||||||
location: 'Location',
|
location: 'Location',
|
||||||
@@ -563,7 +566,6 @@ export default {
|
|||||||
you_could_not_delete_account_has_child_accounts:
|
you_could_not_delete_account_has_child_accounts:
|
||||||
'You could not delete account has child accounts.',
|
'You could not delete account has child accounts.',
|
||||||
journal_entry: 'Journal Entry',
|
journal_entry: 'Journal Entry',
|
||||||
|
|
||||||
estimate: 'Estimate #',
|
estimate: 'Estimate #',
|
||||||
estimate_date: 'Estimate Date',
|
estimate_date: 'Estimate Date',
|
||||||
expiration_date: 'Expiration Date',
|
expiration_date: 'Expiration Date',
|
||||||
@@ -586,7 +588,6 @@ export default {
|
|||||||
rate: 'Rate',
|
rate: 'Rate',
|
||||||
estimate_list: 'Estimate List',
|
estimate_list: 'Estimate List',
|
||||||
estimate_number: 'Estimate Number',
|
estimate_number: 'Estimate Number',
|
||||||
|
|
||||||
product_and_service: 'Product/Service',
|
product_and_service: 'Product/Service',
|
||||||
the_estimate_has_been_successfully_edited:
|
the_estimate_has_been_successfully_edited:
|
||||||
'The estimate #{number} has been successfully edited.',
|
'The estimate #{number} has been successfully edited.',
|
||||||
@@ -594,9 +595,7 @@ export default {
|
|||||||
'The estimate #{number} has been successfully created.',
|
'The estimate #{number} has been successfully created.',
|
||||||
the_estimate_has_been_successfully_deleted:
|
the_estimate_has_been_successfully_deleted:
|
||||||
'The estimate has been successfully deleted.',
|
'The estimate has been successfully deleted.',
|
||||||
|
|
||||||
once_delete_this_estimate_you_will_able_to_restore_it: `Once you delete this estimate, you won\'t be able to restore it later. Are you sure you want to delete this estimate?`,
|
once_delete_this_estimate_you_will_able_to_restore_it: `Once you delete this estimate, you won\'t be able to restore it later. Are you sure you want to delete this estimate?`,
|
||||||
|
|
||||||
cannot_be_zero_or_empty: 'cannot be zero or empty.',
|
cannot_be_zero_or_empty: 'cannot be zero or empty.',
|
||||||
invocies: 'Invoices',
|
invocies: 'Invoices',
|
||||||
invoices_list: 'Invoices List',
|
invoices_list: 'Invoices List',
|
||||||
@@ -609,21 +608,21 @@ export default {
|
|||||||
due_date_: 'Due date',
|
due_date_: 'Due date',
|
||||||
invoice_message: 'Invoice Message',
|
invoice_message: 'Invoice Message',
|
||||||
reference_no: 'Reference No',
|
reference_no: 'Reference No',
|
||||||
|
invocie_number: 'Invoice Number',
|
||||||
|
invoice_amount: 'Invoice Amount',
|
||||||
|
amount_due: 'Amount Due',
|
||||||
|
payment_amount: 'Payment Amount',
|
||||||
edit_invoice: 'Edit Invoice',
|
edit_invoice: 'Edit Invoice',
|
||||||
delete_invoice: 'Delete Invoice',
|
delete_invoice: 'Delete Invoice',
|
||||||
new_invoice: 'New Invoice',
|
new_invoice: 'New Invoice',
|
||||||
invoice_list: 'Invoice List',
|
invoice_list: 'Invoice List',
|
||||||
|
|
||||||
the_invoice_has_been_successfully_edited:
|
the_invoice_has_been_successfully_edited:
|
||||||
'The invoice #{number} has been successfully edited.',
|
'The invoice #{number} has been successfully edited.',
|
||||||
the_invocie_has_been_successfully_created:
|
the_invocie_has_been_successfully_created:
|
||||||
'The invoice #{number} has been successfully created.',
|
'The invoice #{number} has been successfully created.',
|
||||||
the_invocie_has_been_successfully_deleted:
|
the_invocie_has_been_successfully_deleted:
|
||||||
'The invoice has been successfully deleted.',
|
'The invoice has been successfully deleted.',
|
||||||
|
|
||||||
once_delete_this_invoice_you_will_able_to_restore_it: `Once you delete this invoice, you won\'t be able to restore it later. Are you sure you want to delete this invoice?`,
|
once_delete_this_invoice_you_will_able_to_restore_it: `Once you delete this invoice, you won\'t be able to restore it later. Are you sure you want to delete this invoice?`,
|
||||||
|
|
||||||
receipt_list: 'Receipt List',
|
receipt_list: 'Receipt List',
|
||||||
receipts: 'Receipts',
|
receipts: 'Receipts',
|
||||||
receipt: 'Receipt #',
|
receipt: 'Receipt #',
|
||||||
@@ -640,16 +639,13 @@ export default {
|
|||||||
deposit_account: 'Deposit Account',
|
deposit_account: 'Deposit Account',
|
||||||
send_to_email: 'Send to email',
|
send_to_email: 'Send to email',
|
||||||
select_deposit_account: 'Select Deposit Account',
|
select_deposit_account: 'Select Deposit Account',
|
||||||
|
|
||||||
once_delete_this_receipt_you_will_able_to_restore_it: `Once you delete this receipt, you won\'t be able to restore it later. Are you sure you want to delete this receipt?`,
|
once_delete_this_receipt_you_will_able_to_restore_it: `Once you delete this receipt, you won\'t be able to restore it later. Are you sure you want to delete this receipt?`,
|
||||||
|
|
||||||
the_receipt_has_been_successfully_created:
|
the_receipt_has_been_successfully_created:
|
||||||
'The recepit has been successfully created.',
|
'The recepit has been successfully created.',
|
||||||
the_receipt_has_been_successfully_edited:
|
the_receipt_has_been_successfully_edited:
|
||||||
'The receipt has been successfully edited.',
|
'The receipt has been successfully edited.',
|
||||||
the_receipt_has_been_successfully_deleted:
|
the_receipt_has_been_successfully_deleted:
|
||||||
'The receipt has been successfully deleted.',
|
'The receipt has been successfully deleted.',
|
||||||
|
|
||||||
bill_list: 'Bill List',
|
bill_list: 'Bill List',
|
||||||
bills: 'Bills',
|
bills: 'Bills',
|
||||||
accept: 'Accept',
|
accept: 'Accept',
|
||||||
@@ -665,27 +661,57 @@ export default {
|
|||||||
bill_number_: 'Bill number',
|
bill_number_: 'Bill number',
|
||||||
vendor_name_: 'Vendor name',
|
vendor_name_: 'Vendor name',
|
||||||
delete_bill: 'Delete Bill',
|
delete_bill: 'Delete Bill',
|
||||||
|
|
||||||
the_bill_has_been_successfully_edited:
|
the_bill_has_been_successfully_edited:
|
||||||
'The bill #{number} has been successfully edited.',
|
'The bill #{number} has been successfully edited.',
|
||||||
|
|
||||||
the_bill_has_been_successfully_created:
|
the_bill_has_been_successfully_created:
|
||||||
'The bill has been successfully created.',
|
'The bill has been successfully created.',
|
||||||
|
|
||||||
the_bill_has_been_successfully_deleted:
|
the_bill_has_been_successfully_deleted:
|
||||||
'The bill has been successfully deleted.',
|
'The bill has been successfully deleted.',
|
||||||
|
|
||||||
once_delete_this_bill_you_will_able_to_restore_it: `Once you delete this bill, you won\'t be able to restore it later. Are you sure you want to delete this bill?`,
|
once_delete_this_bill_you_will_able_to_restore_it: `Once you delete this bill, you won\'t be able to restore it later. Are you sure you want to delete this bill?`,
|
||||||
|
deposit_to: 'Deposit to',
|
||||||
edit_payment_receive: 'Edit Payment Receive',
|
edit_payment_receive: 'Edit Payment Receive',
|
||||||
|
delete_payment_receive: 'Delete Payment Receive',
|
||||||
|
payment_Receive_list: 'Payment Receive List',
|
||||||
|
payment_receive: 'Payment Receive',
|
||||||
new_payment_receive: 'New Payment Receive',
|
new_payment_receive: 'New Payment Receive',
|
||||||
payment_receives: 'Payment Receives',
|
payment_receives: 'Payment Receives',
|
||||||
payment_receive_no: 'Payment Receive #',
|
payment_receive_no: 'Payment Receive #',
|
||||||
payment_receive_no_: 'Payment receive no',
|
payment_receive_no_: 'Payment receive no',
|
||||||
|
receive_amount: 'Receive Amount',
|
||||||
|
receive_amount_: 'Receive amount',
|
||||||
the_payment_receive_has_been_successfully_created:
|
the_payment_receive_has_been_successfully_created:
|
||||||
'The payment receive has been successfully created.',
|
'The payment receive has been successfully created.',
|
||||||
|
the_payment_receive_has_been_successfully_deleted:
|
||||||
|
'The payment receive has been successfully deleted.',
|
||||||
|
the_payment_receive_has_been_successfully_edited:
|
||||||
|
'The payment receive #{number} has been successfully edited.',
|
||||||
|
once_delete_this_payment_receive_you_will_able_to_restore_it: `Once you delete this payment receive, you won\'t be able to restore it later. Are you sure you want to delete this payment receive?`,
|
||||||
select_invoice: 'Select Invoice',
|
select_invoice: 'Select Invoice',
|
||||||
|
payment_made: 'Payment Made',
|
||||||
payment_mades: 'Payment Mades',
|
edit_payment_made: 'Edit Payment Made',
|
||||||
|
delete_payment_made: 'Delete Payment Made',
|
||||||
|
vendor_name: 'Vendor Name',
|
||||||
|
payment_number: 'Payment Number',
|
||||||
|
payment_no: 'Payment Number #',
|
||||||
|
vendor_name_: 'Vendor name',
|
||||||
|
bill_amount: 'Bill Amount',
|
||||||
|
payment_account_: 'Payment account',
|
||||||
|
payment_no_: 'Payment number',
|
||||||
|
new_payment_made: 'New Payment Made',
|
||||||
|
payment_made_list: 'Payment Made List',
|
||||||
|
payment_account: 'Payment Account',
|
||||||
|
select_vender_account: 'Select Vender Account',
|
||||||
|
select_payment_account: 'Select Payment Account',
|
||||||
|
the_payment_made_has_been_successfully_edited:
|
||||||
|
'The payment made has been successfully edited.',
|
||||||
|
the_payment_made_has_been_successfully_created:
|
||||||
|
'The payment made has been successfully created.',
|
||||||
|
the_payment_made_has_been_successfully_deleted:
|
||||||
|
'The payment made has been successfully deleted.',
|
||||||
|
once_delete_this_payment_made_you_will_able_to_restore_it: `Once you delete this payment made, you won\'t be able to restore it later. Are you sure you want to delete this payment made?`,
|
||||||
|
sellable: 'Sellable',
|
||||||
|
purchasable: 'Purchasable',
|
||||||
|
sell_account: 'Sell Account',
|
||||||
|
cost_account: 'Cost Account',
|
||||||
|
inventory_account: 'Inventory Account',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -292,6 +292,14 @@ export default [
|
|||||||
}),
|
}),
|
||||||
breadcrumb: 'New Payment Receive',
|
breadcrumb: 'New Payment Receive',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: `/payment-receives`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () =>
|
||||||
|
import('containers/Sales/PaymentReceive/PaymentReceiveList'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Payment Receive List',
|
||||||
|
},
|
||||||
|
|
||||||
//Bills
|
//Bills
|
||||||
{
|
{
|
||||||
@@ -322,4 +330,36 @@ export default [
|
|||||||
}),
|
}),
|
||||||
breadcrumb: 'Receipt List',
|
breadcrumb: 'Receipt List',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Payment Mades
|
||||||
|
|
||||||
|
{
|
||||||
|
path: `/payment-made/:id/edit`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Purchases/PaymentMades/PaymentMade'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Edit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/payment-made/new`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Purchases/PaymentMades/PaymentMade'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'New Payment Made',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/payment-mades`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () =>
|
||||||
|
import('containers/Purchases/PaymentMades/PaymentMadeList'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Payment Made List',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -66,27 +66,7 @@ export const deleteBill = ({ id }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const submitBill = ({ form }) => {
|
export const submitBill = ({ form }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) => ApiService.post('purchases/bills', form);
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post('purchases/bills', form)
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const { response } = error;
|
|
||||||
const { data } = response;
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
reject(data?.errors);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchBill = ({ id }) => {
|
export const fetchBill = ({ id }) => {
|
||||||
@@ -94,12 +74,11 @@ export const fetchBill = ({ id }) => {
|
|||||||
new Promise((resovle, reject) => {
|
new Promise((resovle, reject) => {
|
||||||
ApiService.get(`purchases/bills/${id}`)
|
ApiService.get(`purchases/bills/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
const { bill } = response.data;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.BILL_SET,
|
type: t.BILL_SET,
|
||||||
payload: {
|
payload: { id, bill },
|
||||||
id,
|
|
||||||
bill: response.data.bill,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
resovle(response);
|
resovle(response);
|
||||||
})
|
})
|
||||||
@@ -112,25 +91,5 @@ export const fetchBill = ({ id }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const editBill = (id, form) => {
|
export const editBill = (id, form) => {
|
||||||
return (dispatch) =>
|
return (dispatch) => ApiService.post(`purchases/bills/${id}`, form);
|
||||||
new Promise((resolve, rejcet) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post(`purchases/bills/${id}`, form)
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const { response } = error;
|
|
||||||
const { data } = response;
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
rejcet(data?.errors);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,9 +37,13 @@ const reducer = createReducer(initialState, {
|
|||||||
[t.BILLS_ITEMS_SET]: (state, action) => {
|
[t.BILLS_ITEMS_SET]: (state, action) => {
|
||||||
const { bills } = action.payload;
|
const { bills } = action.payload;
|
||||||
const _bills = {};
|
const _bills = {};
|
||||||
|
|
||||||
bills.forEach((bill) => {
|
bills.forEach((bill) => {
|
||||||
|
const oldBill = state.items[bill.id] || {};
|
||||||
|
|
||||||
_bills[bill.id] = {
|
_bills[bill.id] = {
|
||||||
...defaultBill,
|
...defaultBill,
|
||||||
|
...oldBill,
|
||||||
...bill,
|
...bill,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -59,7 +63,6 @@ const reducer = createReducer(initialState, {
|
|||||||
|
|
||||||
[t.BILLS_PAGE_SET]: (state, action) => {
|
[t.BILLS_PAGE_SET]: (state, action) => {
|
||||||
const { customViewId, bills, pagination } = action.payload;
|
const { customViewId, bills, pagination } = action.payload;
|
||||||
|
|
||||||
const viewId = customViewId || -1;
|
const viewId = customViewId || -1;
|
||||||
const view = state.views[viewId] || {};
|
const view = state.views[viewId] || {};
|
||||||
|
|
||||||
@@ -95,8 +98,6 @@ const reducer = createReducer(initialState, {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createTableQueryReducers('bills', reducer);
|
export default createTableQueryReducers('bills', reducer);
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,37 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
const billTableQuery = (state) => {
|
const billTableQuery = (state) => state.bills.tableQuery;
|
||||||
return state.bills.tableQuery;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBillTableQuery = createSelector(
|
|
||||||
paginationLocationQuery,
|
|
||||||
billTableQuery,
|
|
||||||
(locationQuery, tableQuery) => {
|
|
||||||
return {
|
|
||||||
...locationQuery,
|
|
||||||
...tableQuery,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const billPageSelector = (state, props, query) => {
|
const billPageSelector = (state, props, query) => {
|
||||||
const viewId = state.bills.currentViewId;
|
const viewId = state.bills.currentViewId;
|
||||||
return state.bills.views?.[viewId]?.pages?.[query.page];
|
return state.bills.views?.[viewId]?.pages?.[query.page];
|
||||||
};
|
};
|
||||||
|
const billItemsSelector = (state) => state.bills.items;
|
||||||
|
|
||||||
const billItemsSelector = (state) => {
|
const billByIdSelector = (state, props) => state.bills.items[props.billId];
|
||||||
return state.bills.items;
|
|
||||||
|
const billPaginationSelector = (state, props) => {
|
||||||
|
const viewId = state.bills.currentViewId;
|
||||||
|
return state.bills.views?.[viewId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getBillTableQueryFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
billTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current page bills items.
|
||||||
|
* @return {Array}
|
||||||
|
*/
|
||||||
export const getBillCurrentPageFactory = () =>
|
export const getBillCurrentPageFactory = () =>
|
||||||
createSelector(billPageSelector, billItemsSelector, (billPage, billItems) => {
|
createSelector(billPageSelector, billItemsSelector, (billPage, billItems) => {
|
||||||
return typeof billPage === 'object'
|
return typeof billPage === 'object'
|
||||||
@@ -32,20 +39,14 @@ export const getBillCurrentPageFactory = () =>
|
|||||||
: [];
|
: [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const billByIdSelector = (state, props) => {
|
/**
|
||||||
return state.bills.items[props.billId];
|
* Retrieve bill details of the given bill id.
|
||||||
};
|
*/
|
||||||
|
|
||||||
export const getBillByIdFactory = () =>
|
export const getBillByIdFactory = () =>
|
||||||
createSelector(billByIdSelector, (bill) => {
|
createSelector(billByIdSelector, (bill) => {
|
||||||
return bill;
|
return bill;
|
||||||
});
|
});
|
||||||
|
|
||||||
const billPaginationSelector = (state, props) => {
|
|
||||||
const viewId = state.bills.currentViewId;
|
|
||||||
return state.bills.views?.[viewId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBillPaginationMetaFactory = () =>
|
export const getBillPaginationMetaFactory = () =>
|
||||||
createSelector(billPaginationSelector, (billPage) => {
|
createSelector(billPaginationSelector, (billPage) => {
|
||||||
return billPage?.paginationMeta || {};
|
return billPage?.paginationMeta || {};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export const submitEstimate = ({ form }) => {
|
|||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
});
|
});
|
||||||
ApiService.post('sales/estimates', form)
|
ApiService.post('sales/estimates', form)
|
||||||
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
@@ -21,7 +20,6 @@ export const submitEstimate = ({ form }) => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
});
|
});
|
||||||
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -30,23 +28,14 @@ export const submitEstimate = ({ form }) => {
|
|||||||
export const editEstimate = (id, form) => {
|
export const editEstimate = (id, form) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post(`sales/estimates/${id}`, form)
|
ApiService.post(`sales/estimates/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -74,12 +63,12 @@ export const fetchEstimate = ({ id }) => {
|
|||||||
new Promise((resovle, reject) => {
|
new Promise((resovle, reject) => {
|
||||||
ApiService.get(`sales/estimates/${id}`)
|
ApiService.get(`sales/estimates/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
const { estimate } = response.data;
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ESTIMATE_SET,
|
type: t.ESTIMATE_SET,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
estimate: response.data.estimate,
|
estimate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resovle(response);
|
resovle(response);
|
||||||
@@ -95,7 +84,7 @@ export const fetchEstimate = ({ id }) => {
|
|||||||
export const fetchEstimatesTable = ({ query = {} }) => {
|
export const fetchEstimatesTable = ({ query = {} }) => {
|
||||||
return (dispatch, getState) =>
|
return (dispatch, getState) =>
|
||||||
new Promise((resolve, rejcet) => {
|
new Promise((resolve, rejcet) => {
|
||||||
const pageQuery = getState().sales_estimates.tableQuery;
|
const pageQuery = getState().salesEstimates.tableQuery;
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ESTIMATES_TABLE_LOADING,
|
type: t.ESTIMATES_TABLE_LOADING,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -106,7 +95,6 @@ export const fetchEstimatesTable = ({ query = {} }) => {
|
|||||||
params: { ...pageQuery, ...query },
|
params: { ...pageQuery, ...query },
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// debugger;
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ESTIMATES_PAGE_SET,
|
type: t.ESTIMATES_PAGE_SET,
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const reducer = createReducer(initialState, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[t.ESTIMATES_PAGE_SET]: (state, action) => {
|
[t.ESTIMATES_PAGE_SET]: (state, action) => {
|
||||||
|
// @todo camelCase keys.
|
||||||
const { customViewId, sales_estimates, pagination } = action.payload;
|
const { customViewId, sales_estimates, pagination } = action.payload;
|
||||||
|
|
||||||
const viewId = customViewId || -1;
|
const viewId = customViewId || -1;
|
||||||
|
|||||||
@@ -1,27 +1,33 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
const estimateTableQuery = (state) => {
|
const estimateTableQuery = (state) => state.salesEstimates.tableQuery;
|
||||||
return state.sales_estimates.tableQuery;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEstimatesTableQuery = createSelector(
|
const estimateByIdSelector = (state, props) =>
|
||||||
paginationLocationQuery,
|
state.salesEstimates.items[props.estimateId];
|
||||||
estimateTableQuery,
|
|
||||||
(locationQuery, tableQuery) => {
|
const estimatesCurrentViewSelector = (state, props) => {
|
||||||
return {
|
const viewId = state.salesEstimates.currentViewId;
|
||||||
...locationQuery,
|
return state.salesEstimates.views?.[viewId];
|
||||||
...tableQuery,
|
};
|
||||||
};
|
const estimateItemsSelector = (state) => state.salesEstimates.items;
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const estimatesPageSelector = (state, props, query) => {
|
const estimatesPageSelector = (state, props, query) => {
|
||||||
const viewId = state.sales_estimates.currentViewId;
|
const viewId = state.salesEstimates.currentViewId;
|
||||||
return state.sales_estimates.views?.[viewId]?.pages?.[query.page];
|
return state.salesEstimates.views?.[viewId]?.pages?.[query.page];
|
||||||
};
|
};
|
||||||
|
|
||||||
const estimateItemsSelector = (state) => state.sales_estimates.items;
|
export const getEstimatesTableQueryFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
estimateTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const getEstimateCurrentPageFactory = () =>
|
export const getEstimateCurrentPageFactory = () =>
|
||||||
createSelector(
|
createSelector(
|
||||||
@@ -34,21 +40,12 @@ export const getEstimateCurrentPageFactory = () =>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const estimateByIdSelector = (state, props) => {
|
|
||||||
return state.sales_estimates.items[props.estimateId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEstimateByIdFactory = () =>
|
export const getEstimateByIdFactory = () =>
|
||||||
createSelector(estimateByIdSelector, (estimate) => {
|
createSelector(estimateByIdSelector, (estimate) => {
|
||||||
return estimate;
|
return estimate;
|
||||||
});
|
});
|
||||||
|
|
||||||
const estimatesPaginationSelector = (state, props) => {
|
|
||||||
const viewId = state.sales_estimates.currentViewId;
|
|
||||||
return state.sales_estimates.views?.[viewId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEstimatesPaginationMetaFactory = () =>
|
export const getEstimatesPaginationMetaFactory = () =>
|
||||||
createSelector(estimatesPaginationSelector, (estimatePage) => {
|
createSelector(estimatesCurrentViewSelector, (estimateView) => {
|
||||||
return estimatePage?.paginationMeta || {};
|
return estimateView?.paginationMeta || {};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,22 +4,13 @@ import t from 'store/types';
|
|||||||
export const submitInvoice = ({ form }) => {
|
export const submitInvoice = ({ form }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post('sales/invoices', form)
|
ApiService.post('sales/invoices', form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
@@ -51,18 +42,12 @@ export const editInvoice = (id, form) => {
|
|||||||
});
|
});
|
||||||
ApiService.post(`sales/invoices/${id}`, form)
|
ApiService.post(`sales/invoices/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -71,7 +56,7 @@ export const editInvoice = (id, form) => {
|
|||||||
export const fetchInvoicesTable = ({ query } = {}) => {
|
export const fetchInvoicesTable = ({ query } = {}) => {
|
||||||
return (dispatch, getState) =>
|
return (dispatch, getState) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const pageQuery = getState().sales_invoices.tableQuery;
|
const pageQuery = getState().salesInvoices.tableQuery;
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.INVOICES_TABLE_LOADING,
|
type: t.INVOICES_TABLE_LOADING,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -90,6 +75,7 @@ export const fetchInvoicesTable = ({ query } = {}) => {
|
|||||||
customViewId: response.data.customViewId || -1,
|
customViewId: response.data.customViewId || -1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.INVOICES_ITEMS_SET,
|
type: t.INVOICES_ITEMS_SET,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -138,3 +124,26 @@ export const fetchInvoice = ({ id }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const dueInvoices = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.get(`sales/invoices/due_invoices`, {
|
||||||
|
params: { customer_id: id },
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.DUE_INVOICES_SET,
|
||||||
|
payload: {
|
||||||
|
customer_id: id,
|
||||||
|
due_sales_invoices: response.data.due_sales_invoices,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const initialState = {
|
|||||||
page_size: 5,
|
page_size: 5,
|
||||||
page: 1,
|
page: 1,
|
||||||
},
|
},
|
||||||
|
dueInvoices: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultInvoice = {
|
const defaultInvoice = {
|
||||||
@@ -22,13 +23,13 @@ const reducer = createReducer(initialState, {
|
|||||||
[t.INVOICE_SET]: (state, action) => {
|
[t.INVOICE_SET]: (state, action) => {
|
||||||
const { id, sale_invoice } = action.payload;
|
const { id, sale_invoice } = action.payload;
|
||||||
const _invoice = state.items[id] || {};
|
const _invoice = state.items[id] || {};
|
||||||
|
|
||||||
state.items[id] = { ...defaultInvoice, ..._invoice, ...sale_invoice };
|
state.items[id] = { ...defaultInvoice, ..._invoice, ...sale_invoice };
|
||||||
},
|
},
|
||||||
|
|
||||||
[t.INVOICES_ITEMS_SET]: (state, action) => {
|
[t.INVOICES_ITEMS_SET]: (state, action) => {
|
||||||
const { sales_invoices } = action.payload;
|
const { sales_invoices } = action.payload;
|
||||||
const _invoices = {};
|
const _invoices = {};
|
||||||
|
|
||||||
sales_invoices.forEach((invoice) => {
|
sales_invoices.forEach((invoice) => {
|
||||||
_invoices[invoice.id] = {
|
_invoices[invoice.id] = {
|
||||||
...defaultInvoice,
|
...defaultInvoice,
|
||||||
@@ -96,6 +97,40 @@ const reducer = createReducer(initialState, {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
[t.DUE_INVOICES_SET]: (state, action) => {
|
||||||
|
const { customer_id, due_sales_invoices } = action.payload;
|
||||||
|
|
||||||
|
const _dueInvoices = [];
|
||||||
|
|
||||||
|
state.dueInvoices[customer_id] = due_sales_invoices.map((due) => due.id);
|
||||||
|
const _invoices = {};
|
||||||
|
due_sales_invoices.forEach((invoice) => {
|
||||||
|
_invoices[invoice.id] = {
|
||||||
|
...invoice,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
state.items = {
|
||||||
|
...state.dueInvoices,
|
||||||
|
...state.items.dueInvoices,
|
||||||
|
..._invoices,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[t.RELOAD_INVOICES]: (state, action) => {
|
||||||
|
const { sales_invoices } = action.payload;
|
||||||
|
|
||||||
|
const _sales_invoices = {};
|
||||||
|
sales_invoices.forEach((invoice) => {
|
||||||
|
_sales_invoices[invoice.id] = {
|
||||||
|
...invoice,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
state.items = {
|
||||||
|
...state.items,
|
||||||
|
..._sales_invoices,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createTableQueryReducers('sales_invoices', reducer);
|
export default createTableQueryReducers('sales_invoices', reducer);
|
||||||
|
|||||||
@@ -1,27 +1,38 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
import {
|
||||||
|
pickItemsFromIds,
|
||||||
const invoiceTableQuery = (state) => state.sales_invoices.tableQuery;
|
|
||||||
|
|
||||||
export const getInvoiceTableQuery = createSelector(
|
|
||||||
paginationLocationQuery,
|
paginationLocationQuery,
|
||||||
invoiceTableQuery,
|
getItemById,
|
||||||
(locationQuery, tableQuery) => {
|
} from 'store/selectors';
|
||||||
return {
|
|
||||||
...locationQuery,
|
const invoiceTableQuery = (state) => state.salesInvoices.tableQuery;
|
||||||
...tableQuery,
|
|
||||||
};
|
const invoicesByIdSelector = (state, props) =>
|
||||||
},
|
state.salesInvoices.items[props.invoiceId];
|
||||||
);
|
|
||||||
|
const invoicesPaginationSelector = (state, props) => {
|
||||||
|
const viewId = state.salesInvoices.currentViewId;
|
||||||
|
return state.salesInvoices.views?.[viewId];
|
||||||
|
};
|
||||||
|
|
||||||
const invoicesPageSelector = (state, props, query) => {
|
const invoicesPageSelector = (state, props, query) => {
|
||||||
const viewId = state.sales_invoices.currentViewId;
|
const viewId = state.salesInvoices.currentViewId;
|
||||||
return state.sales_invoices.views?.[viewId]?.pages?.[query.page];
|
return state.salesInvoices.views?.[viewId]?.pages?.[query.page];
|
||||||
};
|
};
|
||||||
|
|
||||||
const invoicesItemsSelector = (state) => {
|
const invoicesItemsSelector = (state) => state.salesInvoices.items;
|
||||||
return state.sales_invoices.items;
|
|
||||||
};
|
export const getInvoiceTableQueryFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
invoiceTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const getInvoiceCurrentPageFactory = () =>
|
export const getInvoiceCurrentPageFactory = () =>
|
||||||
createSelector(
|
createSelector(
|
||||||
@@ -34,21 +45,27 @@ export const getInvoiceCurrentPageFactory = () =>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const invoicesByIdSelector = (state, props) => {
|
|
||||||
return state.sales_invoices.items[props.invoiceId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getInvoiecsByIdFactory = () =>
|
export const getInvoiecsByIdFactory = () =>
|
||||||
createSelector(invoicesByIdSelector, (invoice) => {
|
createSelector(invoicesByIdSelector, (invoice) => {
|
||||||
return invoice;
|
return invoice;
|
||||||
});
|
});
|
||||||
|
|
||||||
const invoicesPaginationSelector = (state, props) => {
|
|
||||||
const viewId = state.sales_invoices.currentViewId;
|
|
||||||
return state.sales_invoices.views?.[viewId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getInvoicePaginationMetaFactory = () =>
|
export const getInvoicePaginationMetaFactory = () =>
|
||||||
createSelector(invoicesPaginationSelector, (invoicePage) => {
|
createSelector(invoicesPaginationSelector, (invoicePage) => {
|
||||||
return invoicePage?.paginationMeta || {};
|
return invoicePage?.paginationMeta || {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dueInvoicesSelector = (state, props) => {
|
||||||
|
return state.salesInvoices.dueInvoices[props.customer_id] || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getdueInvoices = createSelector(
|
||||||
|
dueInvoicesSelector,
|
||||||
|
invoicesItemsSelector,
|
||||||
|
(customerIds, items) => {
|
||||||
|
|
||||||
|
return typeof customerIds === 'object'
|
||||||
|
? pickItemsFromIds(items, customerIds) || []
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ export default {
|
|||||||
INVOICES_PAGINATION_SET: 'INVOICES_PAGINATION_SET',
|
INVOICES_PAGINATION_SET: 'INVOICES_PAGINATION_SET',
|
||||||
INVOICES_PAGE_SET: 'INVOICES_PAGE_SET',
|
INVOICES_PAGE_SET: 'INVOICES_PAGE_SET',
|
||||||
INVOICES_ITEMS_SET: 'INVOICES_ITEMS_SET',
|
INVOICES_ITEMS_SET: 'INVOICES_ITEMS_SET',
|
||||||
|
DUE_INVOICES_SET: 'DUE_INVOICES_SET',
|
||||||
|
RELOAD_INVOICES: 'RELOAD_INVOICES',
|
||||||
};
|
};
|
||||||
|
|||||||
128
client/src/store/PaymentMades/paymentMade.actions.js
Normal file
128
client/src/store/PaymentMades/paymentMade.actions.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import ApiService from 'services/ApiService';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
export const submitPaymentMade = ({ form }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ApiService.post('purchases/bill_payments', form)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePaymentMade = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.delete(`purchases/bill_payments/${id}`)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({ type: t.PAYMENT_MADE_DELETE, payload: { id } });
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response.data.errors || []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editPaymentMade = (id, form) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
ApiService.post(`purchases/bill_payments/${id}`, form)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
|
||||||
|
rejcet(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPaymentMadesTable = ({ query = {} }) => {
|
||||||
|
return (dispatch, getState) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
const pageQuery = getState().paymentMades.tableQuery;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADES_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ApiService.get('purchases/bill_payments', {
|
||||||
|
params: { ...pageQuery, ...query },
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADES_PAGE_SET,
|
||||||
|
payload: {
|
||||||
|
bill_payments: response.data.bill_payments.results,
|
||||||
|
pagination: response.data.bill_payments.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADES_ITEMS_SET,
|
||||||
|
payload: {
|
||||||
|
bill_payments: response.data.bill_payments.results,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADES_PAGINATION_SET,
|
||||||
|
payload: {
|
||||||
|
pagination: response.data.bill_payments.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADES_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
rejcet(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPaymentMade = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.get(`purchases/bill_payments/${id}`, {})
|
||||||
|
.then((response) => {
|
||||||
|
// dispatch({
|
||||||
|
// type: t.RELOAD_INVOICES,
|
||||||
|
// payload: {
|
||||||
|
// sales_invoices: response.data.paymentReceive.entries.map(
|
||||||
|
// (e) => e.invoice,
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_MADE_SET,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
bill_payment: response.data.bill_payment,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
88
client/src/store/PaymentMades/paymentMade.reducer.js
Normal file
88
client/src/store/PaymentMades/paymentMade.reducer.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
|
import { createTableQueryReducers } from 'store/queryReducers';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
items: {},
|
||||||
|
views: {},
|
||||||
|
loading: false,
|
||||||
|
currentViewId: -1,
|
||||||
|
tableQuery: {
|
||||||
|
page_size: 5,
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultPaymentMade = {
|
||||||
|
entries: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = createReducer(initialState, {
|
||||||
|
[t.PAYMENT_MADES_TABLE_LOADING]: (state, action) => {
|
||||||
|
const { loading } = action.payload;
|
||||||
|
state.loading = loading;
|
||||||
|
},
|
||||||
|
[t.PAYMENT_MADES_ITEMS_SET]: (state, action) => {
|
||||||
|
const { bill_payments } = action.payload;
|
||||||
|
const _bill_payments = {};
|
||||||
|
bill_payments.forEach((billPayment) => {
|
||||||
|
_bill_payments[billPayment.id] = {
|
||||||
|
...defaultPaymentMade,
|
||||||
|
...billPayment,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
state.items = {
|
||||||
|
...state.items,
|
||||||
|
..._bill_payments,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_MADE_DELETE]: (state, action) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
|
||||||
|
if (typeof state.items[id] !== 'undefined') {
|
||||||
|
delete state.items[id];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_MADES_PAGINATION_SET]: (state, action) => {
|
||||||
|
const { pagination, customViewId } = action.payload;
|
||||||
|
|
||||||
|
const mapped = {
|
||||||
|
pageSize: parseInt(pagination.pageSize, 10),
|
||||||
|
page: parseInt(pagination.page, 10),
|
||||||
|
total: parseInt(pagination.total, 10),
|
||||||
|
};
|
||||||
|
const paginationMeta = {
|
||||||
|
...mapped,
|
||||||
|
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
|
||||||
|
pageIndex: Math.max(mapped.page - 1, 0),
|
||||||
|
};
|
||||||
|
state.views = {
|
||||||
|
...state.views,
|
||||||
|
[customViewId]: {
|
||||||
|
...(state.views?.[customViewId] || {}),
|
||||||
|
paginationMeta,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_MADES_PAGE_SET]: (state, action) => {
|
||||||
|
const { customViewId, bill_payments, pagination } = action.payload;
|
||||||
|
|
||||||
|
const viewId = customViewId || -1;
|
||||||
|
const view = state.views[viewId] || {};
|
||||||
|
|
||||||
|
state.views[viewId] = {
|
||||||
|
...view,
|
||||||
|
pages: {
|
||||||
|
...(state.views?.[viewId]?.pages || {}),
|
||||||
|
[pagination.page]: {
|
||||||
|
ids: bill_payments.map((i) => i.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default createTableQueryReducers('bill_payments', reducer);
|
||||||
56
client/src/store/PaymentMades/paymentMade.selector.js
Normal file
56
client/src/store/PaymentMades/paymentMade.selector.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const paymentMadeTableQuery = (state) => state.paymentMades.tableQuery;
|
||||||
|
|
||||||
|
const paymentMadesPageSelector = (state, props, query) => {
|
||||||
|
const viewId = state.paymentMades.currentViewId;
|
||||||
|
return state.paymentMades.views?.[viewId]?.pages?.[query.page];
|
||||||
|
};
|
||||||
|
|
||||||
|
const paymentMadesItemsSelector = (state) => {
|
||||||
|
return state.paymentMades.items;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PaymentMadePaginationSelector = (state, props) => {
|
||||||
|
const viewId = state.paymentMades.currentViewId;
|
||||||
|
return state.paymentMades.views?.[viewId];
|
||||||
|
};
|
||||||
|
|
||||||
|
const paymentMadesIds = (state, props) => {
|
||||||
|
return state.paymentMades.items[props.paymentMadeId];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPaymentMadeCurrentPageFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paymentMadesPageSelector,
|
||||||
|
paymentMadesItemsSelector,
|
||||||
|
(Page, Items) => {
|
||||||
|
return typeof Page === 'object'
|
||||||
|
? pickItemsFromIds(Items, Page.ids) || []
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getPaymentMadeTableQuery = createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
paymentMadeTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getPaymentMadePaginationMetaFactory = () =>
|
||||||
|
createSelector(PaymentMadePaginationSelector, (Page) => {
|
||||||
|
return Page?.paginationMeta || {};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPaymentMadeByIdFactory = () =>
|
||||||
|
createSelector(paymentMadesIds, (payment_Made) => {
|
||||||
|
return payment_Made;
|
||||||
|
});
|
||||||
11
client/src/store/PaymentMades/paymentMade.type.js
Normal file
11
client/src/store/PaymentMades/paymentMade.type.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
PAYMENT_MADE_LIST_SET: 'PAYMENT_MADE_LIST_SET',
|
||||||
|
PAYMENT_MADE_DELETE: 'PAYMENT_MADE_DELETE',
|
||||||
|
PAYMENT_MADE_SET: 'PAYMENT_MADE_SET',
|
||||||
|
PAYMENT_MADE_SET_CURRENT_VIEW: 'PAYMENT_MADE_SET_CURRENT_VIEW',
|
||||||
|
PAYMENT_MADE_TABLE_QUERIES_ADD: 'PAYMENT_MADE_TABLE_QUERIES_ADD',
|
||||||
|
PAYMENT_MADES_TABLE_LOADING: 'PAYMENT_MADES_TABLE_LOADING',
|
||||||
|
PAYMENT_MADES_PAGE_SET: 'PAYMENT_MADES_PAGE_SET',
|
||||||
|
PAYMENT_MADES_ITEMS_SET: 'PAYMENT_MADES_ITEMS_SET',
|
||||||
|
PAYMENT_MADES_PAGINATION_SET: 'PAYMENT_MADES_PAGINATION_SET',
|
||||||
|
};
|
||||||
@@ -4,22 +4,14 @@ import t from 'store/types';
|
|||||||
export const submitPaymentReceive = ({ form }) => {
|
export const submitPaymentReceive = ({ form }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post('sales/payment_receives', form)
|
ApiService.post('sales/payment_receives', form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -28,22 +20,14 @@ export const submitPaymentReceive = ({ form }) => {
|
|||||||
export const editPaymentReceive = (id, form) => {
|
export const editPaymentReceive = (id, form) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, rejcet) => {
|
new Promise((resolve, rejcet) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post(`sales/payment_receives/${id}`, form)
|
ApiService.post(`sales/payment_receives/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
rejcet(data?.errors);
|
rejcet(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -52,9 +36,9 @@ export const editPaymentReceive = (id, form) => {
|
|||||||
export const deletePaymentReceive = ({ id }) => {
|
export const deletePaymentReceive = ({ id }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resovle, reject) => {
|
new Promise((resovle, reject) => {
|
||||||
ApiService.delete(`payment_receives/${id}`)
|
ApiService.delete(`sales/payment_receives/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({ type: t.PAYMENT_RECEIVE_DELETE });
|
dispatch({ type: t.PAYMENT_RECEIVE_DELETE, payload: { id } });
|
||||||
resovle(response);
|
resovle(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -66,21 +50,31 @@ export const deletePaymentReceive = ({ id }) => {
|
|||||||
export const fetchPaymentReceive = ({ id }) => {
|
export const fetchPaymentReceive = ({ id }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resovle, reject) => {
|
new Promise((resovle, reject) => {
|
||||||
ApiService.get(`payment_receives/${id}`)
|
ApiService.get(`sales/payment_receives/${id}`, {})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.RELOAD_INVOICES,
|
||||||
|
payload: {
|
||||||
|
sales_invoices: response.data.paymentReceive.entries.map(
|
||||||
|
(e) => e.invoice,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.PAYMENT_RECEIVE_SET,
|
type: t.PAYMENT_RECEIVE_SET,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
payment_receive: response.data.payment_receive,
|
paymentReceive: response.data.paymentReceive,
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resovle(response);
|
resovle(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
// const { response } = error;
|
||||||
const { data } = response;
|
// const { data } = response;
|
||||||
reject(data?.errors);
|
// reject(data?.errors);
|
||||||
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -88,7 +82,7 @@ export const fetchPaymentReceive = ({ id }) => {
|
|||||||
export const fetchPaymentReceivesTable = ({ query = {} }) => {
|
export const fetchPaymentReceivesTable = ({ query = {} }) => {
|
||||||
return (dispatch, getState) =>
|
return (dispatch, getState) =>
|
||||||
new Promise((resolve, rejcet) => {
|
new Promise((resolve, rejcet) => {
|
||||||
const pageQuery = getState().payment_receive.tableQuery;
|
const pageQuery = getState().paymentReceives.tableQuery;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.PAYMENT_RECEIVES_TABLE_LOADING,
|
type: t.PAYMENT_RECEIVES_TABLE_LOADING,
|
||||||
@@ -96,12 +90,12 @@ export const fetchPaymentReceivesTable = ({ query = {} }) => {
|
|||||||
loading: true,
|
loading: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
ApiService.get('payment_receives', {
|
ApiService.get('sales/payment_receives', {
|
||||||
params: { ...pageQuery, ...query },
|
params: { ...pageQuery, ...query },
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.RECEIPTS_PAGE_SET,
|
type: t.PAYMENT_RECEIVES_PAGE_SET,
|
||||||
payload: {
|
payload: {
|
||||||
payment_receives: response.data.payment_receives.results,
|
payment_receives: response.data.payment_receives.results,
|
||||||
pagination: response.data.payment_receives.pagination,
|
pagination: response.data.payment_receives.pagination,
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
|
import { createTableQueryReducers } from 'store/queryReducers';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
items: {},
|
||||||
|
views: {},
|
||||||
|
loading: false,
|
||||||
|
currentViewId: -1,
|
||||||
|
tableQuery: {
|
||||||
|
page_size: 5,
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultPaymentReceive = {
|
||||||
|
entries: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = createReducer(initialState, {
|
||||||
|
[t.PAYMENT_RECEIVE_SET]: (state, action) => {
|
||||||
|
const { id, paymentReceive } = action.payload;
|
||||||
|
|
||||||
|
const _paymentReceive = {
|
||||||
|
...paymentReceive,
|
||||||
|
entries: paymentReceive.entries.map((e) => {
|
||||||
|
return { ...omit(e, ['invoice']) };
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const oldPaymentReceive = state.items[id] || {};
|
||||||
|
state.items[id] = {
|
||||||
|
...defaultPaymentReceive,
|
||||||
|
...oldPaymentReceive,
|
||||||
|
..._paymentReceive,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVES_TABLE_LOADING]: (state, action) => {
|
||||||
|
const { loading } = action.payload;
|
||||||
|
state.loading = loading;
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVES_ITEMS_SET]: (state, action) => {
|
||||||
|
const { payment_receives } = action.payload;
|
||||||
|
const _payment_receives = {};
|
||||||
|
payment_receives.forEach((payment_receive) => {
|
||||||
|
_payment_receives[payment_receive.id] = {
|
||||||
|
...defaultPaymentReceive,
|
||||||
|
...payment_receive,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
state.items = {
|
||||||
|
...state.items,
|
||||||
|
..._payment_receives,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVE_SET_CURRENT_VIEW]: (state, action) => {
|
||||||
|
state.currentViewId = action.currentViewId;
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVE_DELETE]: (state, action) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
|
||||||
|
if (typeof state.items[id] !== 'undefined') {
|
||||||
|
delete state.items[id];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVES_PAGINATION_SET]: (state, action) => {
|
||||||
|
const { pagination, customViewId } = action.payload;
|
||||||
|
|
||||||
|
const mapped = {
|
||||||
|
pageSize: parseInt(pagination.pageSize, 10),
|
||||||
|
page: parseInt(pagination.page, 10),
|
||||||
|
total: parseInt(pagination.total, 10),
|
||||||
|
};
|
||||||
|
const paginationMeta = {
|
||||||
|
...mapped,
|
||||||
|
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
|
||||||
|
pageIndex: Math.max(mapped.page - 1, 0),
|
||||||
|
};
|
||||||
|
state.views = {
|
||||||
|
...state.views,
|
||||||
|
[customViewId]: {
|
||||||
|
...(state.views?.[customViewId] || {}),
|
||||||
|
paginationMeta,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[t.PAYMENT_RECEIVES_PAGE_SET]: (state, action) => {
|
||||||
|
const { customViewId, payment_receives, pagination } = action.payload;
|
||||||
|
|
||||||
|
const viewId = customViewId || -1;
|
||||||
|
const view = state.views[viewId] || {};
|
||||||
|
|
||||||
|
state.views[viewId] = {
|
||||||
|
...view,
|
||||||
|
pages: {
|
||||||
|
...(state.views?.[viewId]?.pages || {}),
|
||||||
|
[pagination.page]: {
|
||||||
|
ids: payment_receives.map((i) => i.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default createTableQueryReducers('payment_receives', reducer);
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
|
const paymentReceivesPageSelector = (state, props, query) => {
|
||||||
|
const viewId = state.paymentReceives.currentViewId;
|
||||||
|
return state.paymentReceives.views?.[viewId]?.pages?.[query.page];
|
||||||
|
};
|
||||||
|
|
||||||
|
const paymentReceivesItemsSelector = (state) => {
|
||||||
|
return state.paymentReceives.items;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPaymentReceiveCurrentPageFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paymentReceivesPageSelector,
|
||||||
|
paymentReceivesItemsSelector,
|
||||||
|
(Page, Items) => {
|
||||||
|
return typeof Page === 'object'
|
||||||
|
? pickItemsFromIds(Items, Page.ids) || []
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const paymentReceiveTableQuery = (state) => state.paymentReceives.tableQuery;
|
||||||
|
|
||||||
|
export const getPaymentReceiveTableQuery = createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
paymentReceiveTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const PaymentReceivePaginationSelector = (state, props) => {
|
||||||
|
const viewId = state.paymentReceives.currentViewId;
|
||||||
|
return state.paymentReceives.views?.[viewId];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPaymentReceivePaginationMetaFactory = () =>
|
||||||
|
createSelector(PaymentReceivePaginationSelector, (Page) => {
|
||||||
|
return Page?.paginationMeta || {};
|
||||||
|
});
|
||||||
|
|
||||||
|
const invoicesItems = (state) => {
|
||||||
|
return state.salesInvoices.items;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const payemntReceiveById = (state, props) => {
|
||||||
|
return state.paymentReceives.items[props.paymentReceiveId];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPaymentReceiveByIdFactory = () =>
|
||||||
|
createSelector(payemntReceiveById, (payment_receive) => {
|
||||||
|
return payment_receive;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const paymentReceiveInvoicesIdss = (state, props) => {
|
||||||
|
return state.paymentReceives.items[props.paymentReceiveInvoices]
|
||||||
|
};
|
||||||
|
|
||||||
|
// const invoicesItems = (state) => {
|
||||||
|
// return state.sales_invoices.items;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export const = createSelector(
|
||||||
|
// paymentReceiveInvoicesIds,
|
||||||
|
// invoicesItems,
|
||||||
|
// (ids, items) => {},
|
||||||
|
// );
|
||||||
|
|
||||||
|
export const getPaymentReceiveInvoices = createSelector(
|
||||||
|
payemntReceiveById,
|
||||||
|
invoicesItems,
|
||||||
|
(paymentRecieve, items) => {
|
||||||
|
return typeof paymentRecieve === 'object'
|
||||||
|
? pickItemsFromIds(
|
||||||
|
items,
|
||||||
|
paymentRecieve.entries.map((entry) => entry.invoice_id),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
PAYMENT_RECEIVE_LIST_SET: 'PAYMENT_RECEIVE_LIST_SET',
|
PAYMENT_RECEIVE_LIST_SET: 'PAYMENT_RECEIVE_LIST_SET',
|
||||||
PAYMENT_RECEIVE_SET: 'PAYMENT_RECEIVE_SET',
|
|
||||||
PAYMENT_RECEIVE_DELETE: 'PAYMENT_RECEIVE_DELETE',
|
PAYMENT_RECEIVE_DELETE: 'PAYMENT_RECEIVE_DELETE',
|
||||||
|
PAYMENT_RECEIVE_SET: 'PAYMENT_RECEIVE_SET',
|
||||||
PAYMENT_RECEIVE_SET_CURRENT_VIEW: 'PAYMENT_RECEIVE_SET_CURRENT_VIEW',
|
PAYMENT_RECEIVE_SET_CURRENT_VIEW: 'PAYMENT_RECEIVE_SET_CURRENT_VIEW',
|
||||||
PAYMENT_RECEIVE_TABLE_QUERIES_ADD: 'PAYMENT_RECEIVE_TABLE_QUERIES_ADD',
|
PAYMENT_RECEIVE_TABLE_QUERIES_ADD: 'PAYMENT_RECEIVE_TABLE_QUERIES_ADD',
|
||||||
PAYMENT_RECEIVES_TABLE_LOADING: 'PAYMENT_RECEIVES_TABLE_LOADING',
|
PAYMENT_RECEIVES_TABLE_LOADING: 'PAYMENT_RECEIVES_TABLE_LOADING',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
ITEMS_CATEGORY_DATA_TABLE: 'ITEMS_CATEGORY_DATA_TABLE',
|
ITEMS_CATEGORY_DATA_TABLE: 'ITEMS_CATEGORY_DATA_TABLE',
|
||||||
CATEGORY_DELETE: 'CATEGORY_DELETE',
|
CATEGORY_DELETE: 'CATEGORY_DELETE',
|
||||||
ITEM_CATEGORIES_TABLE_LOADING: 'ITEM_CATEGORIES_TABLE_LOADING',
|
ITEM_CATEGORIES_TABLE_LOADING: 'ITEM_CATEGORIES_TABLE_LOADING',
|
||||||
ITEM_CATEGORIES_BULK_DELETE:'ITEM_CATEGORIES_BULK_DELETE'
|
ITEM_CATEGORIES_BULK_DELETE: 'ITEM_CATEGORIES_BULK_DELETE',
|
||||||
|
ITEM_CATEGORIES_TABLE_QUERIES_ADD: 'ITEM_CATEGORIES_TABLE_QUERIES_ADD',
|
||||||
|
ITEM_CATEGORIES_SET_CURRENT_VIEW: 'ITEM_CATEGORIES_SET_CURRENT_VIEW',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,22 +45,14 @@ export const deleteReceipt = ({ id }) => {
|
|||||||
export const editReceipt = (id, form) => {
|
export const editReceipt = (id, form) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, rejcet) => {
|
new Promise((resolve, rejcet) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.post(`sales/receipts/${id}`, form)
|
ApiService.post(`sales/receipts/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
rejcet(data?.errors);
|
rejcet(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -91,7 +83,7 @@ export const fetchReceipt = ({ id }) => {
|
|||||||
export const fetchReceiptsTable = ({ query = {} }) => {
|
export const fetchReceiptsTable = ({ query = {} }) => {
|
||||||
return (dispatch, getState) =>
|
return (dispatch, getState) =>
|
||||||
new Promise((resolve, rejcet) => {
|
new Promise((resolve, rejcet) => {
|
||||||
const pageQuery = getState().sales_receipts.tableQuery;
|
const pageQuery = getState().salesReceipts.tableQuery;
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.RECEIPTS_TABLE_LOADING,
|
type: t.RECEIPTS_TABLE_LOADING,
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -2,14 +2,22 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
const receiptsPageSelector = (state, props, query) => {
|
const receiptsPageSelector = (state, props, query) => {
|
||||||
const viewId = state.sales_receipts.currentViewId;
|
const viewId = state.salesReceipts.currentViewId;
|
||||||
return state.sales_receipts.views?.[viewId]?.pages?.[query.page];
|
return state.salesReceipts.views?.[viewId]?.pages?.[query.page];
|
||||||
};
|
};
|
||||||
|
|
||||||
const receiptItemsSelector = (state) => {
|
const receiptsPaginationSelector = (state, props) => {
|
||||||
return state.sales_receipts.items;
|
const viewId = state.salesReceipts.currentViewId;
|
||||||
|
return state.salesReceipts.views?.[viewId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const receiptItemsSelector = (state) => state.salesReceipts.items;
|
||||||
|
|
||||||
|
const receiptTableQuery = (state) => state.salesReceipts.tableQuery;
|
||||||
|
|
||||||
|
const receiptByIdSelector = (state, props) => state.salesReceipts.items[props.receiptId];
|
||||||
|
|
||||||
|
|
||||||
export const getReceiptCurrentPageFactory = () =>
|
export const getReceiptCurrentPageFactory = () =>
|
||||||
createSelector(
|
createSelector(
|
||||||
receiptsPageSelector,
|
receiptsPageSelector,
|
||||||
@@ -21,35 +29,23 @@ export const getReceiptCurrentPageFactory = () =>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const receiptTableQuery = (state) => {
|
export const getReceiptsTableQueryFactory = () =>
|
||||||
return state.sales_receipts.tableQuery;
|
createSelector(
|
||||||
};
|
paginationLocationQuery,
|
||||||
|
receiptTableQuery,
|
||||||
export const getReceiptsTableQuery = createSelector(
|
(locationQuery, tableQuery) => {
|
||||||
paginationLocationQuery,
|
return {
|
||||||
receiptTableQuery,
|
...locationQuery,
|
||||||
(locationQuery, tableQuery) => {
|
...tableQuery,
|
||||||
return {
|
};
|
||||||
...locationQuery,
|
},
|
||||||
...tableQuery,
|
);
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const receiptByIdSelector = (state, props) => {
|
|
||||||
return state.sales_receipts.items[props.receiptId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReceiptByIdFactory = () =>
|
export const getReceiptByIdFactory = () =>
|
||||||
createSelector(receiptByIdSelector, (receipt) => {
|
createSelector(receiptByIdSelector, (receipt) => {
|
||||||
return receipt;
|
return receipt;
|
||||||
});
|
});
|
||||||
|
|
||||||
const receiptsPaginationSelector = (state, props) => {
|
|
||||||
const viewId = state.sales_receipts.currentViewId;
|
|
||||||
return state.sales_receipts.views?.[viewId];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReceiptsPaginationMetaFactory = () =>
|
export const getReceiptsPaginationMetaFactory = () =>
|
||||||
createSelector(receiptsPaginationSelector, (receiptPage) => {
|
createSelector(receiptsPaginationSelector, (receiptPage) => {
|
||||||
return receiptPage?.paginationMeta || {};
|
return receiptPage?.paginationMeta || {};
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ import globalSearch from './search/search.reducer';
|
|||||||
import exchangeRates from './ExchangeRate/exchange.reducer';
|
import exchangeRates from './ExchangeRate/exchange.reducer';
|
||||||
import globalErrors from './globalErrors/globalErrors.reducer';
|
import globalErrors from './globalErrors/globalErrors.reducer';
|
||||||
import customers from './customers/customers.reducer';
|
import customers from './customers/customers.reducer';
|
||||||
import sales_estimates from './Estimate/estimates.reducer';
|
import salesEstimates from './Estimate/estimates.reducer';
|
||||||
import sales_invoices from './Invoice/invoices.reducer';
|
import salesInvoices from './Invoice/invoices.reducer';
|
||||||
import sales_receipts from './receipt/receipt.reducer';
|
import salesReceipts from './receipt/receipt.reducer';
|
||||||
import bills from './Bills/bills.reducer';
|
import bills from './Bills/bills.reducer';
|
||||||
import vendors from './vendors/vendors.reducer';
|
import vendors from './vendors/vendors.reducer';
|
||||||
|
import paymentReceives from './PaymentReceive/paymentReceive.reducer';
|
||||||
|
import paymentMades from './PaymentMades/paymentMade.reducer';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
authentication,
|
authentication,
|
||||||
@@ -43,9 +45,12 @@ export default combineReducers({
|
|||||||
exchangeRates,
|
exchangeRates,
|
||||||
globalErrors,
|
globalErrors,
|
||||||
customers,
|
customers,
|
||||||
sales_estimates,
|
|
||||||
sales_invoices,
|
salesEstimates,
|
||||||
sales_receipts,
|
salesInvoices,
|
||||||
|
salesReceipts,
|
||||||
bills,
|
bills,
|
||||||
vendors,
|
vendors,
|
||||||
|
paymentReceives,
|
||||||
|
paymentMades
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import estimates from './Estimate/estimates.types';
|
|||||||
import invoices from './Invoice/invoices.types';
|
import invoices from './Invoice/invoices.types';
|
||||||
import receipts from './receipt/receipt.type';
|
import receipts from './receipt/receipt.type';
|
||||||
import bills from './Bills/bills.type';
|
import bills from './Bills/bills.type';
|
||||||
import paymentReceives from './PaymentReceive/paymentReceive.type';
|
|
||||||
import vendors from './vendors/vendors.types';
|
import vendors from './vendors/vendors.types';
|
||||||
|
import paymentReceives from './PaymentReceive/paymentReceive.type';
|
||||||
|
import paymentMades from './PaymentMades/paymentMade.type';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...authentication,
|
...authentication,
|
||||||
@@ -50,4 +51,5 @@ export default {
|
|||||||
...bills,
|
...bills,
|
||||||
...paymentReceives,
|
...paymentReceives,
|
||||||
...vendors,
|
...vendors,
|
||||||
|
...paymentMades,
|
||||||
};
|
};
|
||||||
|
|||||||
22
client/src/store/vendors/vendors.actions.js
vendored
22
client/src/store/vendors/vendors.actions.js
vendored
@@ -9,9 +9,7 @@ export const fetchVendorsTable = ({ query }) => {
|
|||||||
type: t.VENDORS_TABLE_LOADING,
|
type: t.VENDORS_TABLE_LOADING,
|
||||||
payload: { loading: true },
|
payload: { loading: true },
|
||||||
});
|
});
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
ApiService.get(`vendors`, { params: { ...pageQuery, ...query } })
|
ApiService.get(`vendors`, { params: { ...pageQuery, ...query } })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -22,14 +20,12 @@ export const fetchVendorsTable = ({ query }) => {
|
|||||||
customViewId: response.data.customViewId,
|
customViewId: response.data.customViewId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.VENDORS_ITEMS_SET,
|
type: t.VENDORS_ITEMS_SET,
|
||||||
payload: {
|
payload: {
|
||||||
vendors: response.data.vendors.results,
|
vendors: response.data.vendors.results,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.VENDORS_PAGINATION_SET,
|
type: t.VENDORS_PAGINATION_SET,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -37,14 +33,11 @@ export const fetchVendorsTable = ({ query }) => {
|
|||||||
customViewId: response.data.customViewId || -1,
|
customViewId: response.data.customViewId || -1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.VENDORS_TABLE_LOADING,
|
type: t.VENDORS_TABLE_LOADING,
|
||||||
payload: { loading: false },
|
payload: { loading: false },
|
||||||
});
|
});
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -56,23 +49,16 @@ export const fetchVendorsTable = ({ query }) => {
|
|||||||
export const editVendor = ({ form, id }) => {
|
export const editVendor = ({ form, id }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
|
|
||||||
ApiService.post(`vendors/${id}`, form)
|
ApiService.post(`vendors/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const { response } = error;
|
const { response } = error;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
|
||||||
});
|
|
||||||
reject(data?.errors);
|
reject(data?.errors);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user