Fix: Purchases Bills & feat: BillNumberDialog

This commit is contained in:
elforjani3
2020-10-24 22:20:26 +02:00
parent 173c14bd14
commit d468e69a0d
18 changed files with 283 additions and 66 deletions

View File

@@ -8,13 +8,14 @@ import AccountFormDialog from 'containers/Dialogs/AccountFormDialog';
// import InviteUserDialog from 'containers/Dialogs/InviteUserDialog';
// import ExchangeRateDialog from 'containers/Dialogs/ExchangeRateDialog';
import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog';
import BillNumberDialog from 'containers/Dialogs/BillNumberDialog';
export default function DialogsContainer() {
return (
<div>
<AccountFormDialog dialogName={'account-form'} />
<JournalNumberDialog dialogName={'journal-number-form'} />
<BillNumberDialog dialogName={'bill-number-form'} />
</div>
);
}

View File

@@ -0,0 +1,79 @@
import React from 'react';
import { DialogContent } from 'components';
import { useQuery, queryCache } from 'react-query';
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import withSettings from 'containers/Settings/withSettings';
import withBillActions from 'containers/Purchases/Bill/withBillActions';
import { compose, optionsMapToArray } from 'utils';
/**
* bill number dialog's content.
*/
function BillNumberDialogContent({
// #withSettings
nextNumber,
numberPrefix,
// #withSettingsActions
requestFetchOptions,
requestSubmitOptions,
// #withDialogActions
closeDialog,
// #withBillActions
setBillNumberChanged,
}) {
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'bills' };
});
requestSubmitOptions({ options })
.then(() => {
setSubmitting(false);
closeDialog('bill-number-form');
setBillNumberChanged(true);
setTimeout(() => {
queryCache.invalidateQueries('settings');
}, 250);
})
.catch(() => {
setSubmitting(false);
});
};
const handleClose = () => {
closeDialog('bill-number-form');
};
return (
<DialogContent isLoading={fetchSettings.isFetching}>
<ReferenceNumberForm
initialNumber={nextNumber}
initialPrefix={numberPrefix}
onSubmit={handleSubmitForm}
onClose={handleClose}
/>
</DialogContent>
);
}
export default compose(
withDialogActions,
withSettingsActions,
withSettings(({ billsettings }) => ({
nextNumber: billsettings?.next_number,
numberPrefix: billsettings?.number_prefix,
})),
withBillActions,
)(BillNumberDialogContent);

View File

@@ -0,0 +1,26 @@
import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
const BillNumberDialogContent = lazy(() => import('./BillNumberDialogContent'));
function BillNumberDialog({ dialogName, payload = { id: null }, isOpen }) {
return (
<Dialog
name={dialogName}
title={<T id={'bill_number_settings'} />}
autoFocus={true}
canEscapeKeyClose={true}
isOpen={isOpen}
className={'dialog--journal-number-settings'}
>
<DialogSuspense>
<BillNumberDialogContent billNumberId={payload.id} />
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(BillNumberDialog);

View File

@@ -20,8 +20,10 @@ import BillFormFooter from './BillFormFooter';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withMediaActions from 'containers/Media/withMediaActions';
import withBills from './withBills';
import withBillActions from './withBillActions';
import withBillDetail from './withBillDetail';
import withSettings from 'containers/Settings/withSettings';
import { AppToaster } from 'components';
import Dragzone from 'components/Dragzone';
@@ -29,7 +31,7 @@ import useMedia from 'hooks/useMedia';
import { compose, repeatValue } from 'utils';
const MIN_LINES_NUMBER = 4;
const MIN_LINES_NUMBER = 5;
function BillForm({
//#WithMedia
@@ -39,10 +41,17 @@ function BillForm({
//#withBillActions
requestSubmitBill,
requestEditBill,
setBillNumberChanged,
//#withDashboard
changePageTitle,
changePageSubtitle,
// #withBills
nextBillNumberChanged,
// #withSettings
billNextNumber,
billNumberPrefix,
//#withBillDetail
bill,
@@ -83,7 +92,6 @@ function BillForm({
}
}, [changePageTitle, bill, formatMessage]);
// @todo abstruct validation schema to sperated file.
const validationSchema = Yup.object().shape({
vendor_id: Yup.number()
.required()
@@ -94,11 +102,11 @@ function BillForm({
due_date: Yup.date()
.required()
.label(formatMessage({ id: 'due_date_' })),
bill_number: Yup.number()
bill_number: Yup.string()
.required()
.label(formatMessage({ id: 'bill_number_' })),
reference_no: Yup.string().min(1).max(255),
status: Yup.string().required().nullable(),
reference_no: Yup.string().nullable().min(1).max(255),
// status: Yup.string().required().nullable(),
note: Yup.string()
.trim()
.min(1)
@@ -128,27 +136,34 @@ function BillForm({
[onFormSubmit],
);
const defaultBill = useMemo(() => ({
index: 0,
item_id: null,
rate: null,
discount: 0,
quantity: null,
description: '',
}));
const defaultBill = useMemo(
() => ({
index: 0,
item_id: null,
rate: null,
discount: 0,
quantity: null,
description: '',
}),
[],
);
const billNumber = billNumberPrefix
? `${billNumberPrefix}-${billNextNumber}`
: billNextNumber;
const defaultInitialValues = useMemo(
() => ({
vendor_id: '',
bill_number: '',
bill_number: billNumber,
bill_date: moment(new Date()).format('YYYY-MM-DD'),
due_date: moment(new Date()).format('YYYY-MM-DD'),
status: 'Bill',
// status: 'Bill',
reference_no: '',
note: '',
entries: [...repeatValue(defaultBill, MIN_LINES_NUMBER)],
}),
[defaultBill],
[defaultBill, billNumber],
);
const orderingIndex = (_bill) => {
@@ -209,9 +224,12 @@ function BillForm({
requestEditBill(bill.id, requestForm)
.then((response) => {
AppToaster.show({
message: formatMessage({
id: 'the_bill_has_been_successfully_edited',
}),
message: formatMessage(
{
id: 'the_bill_has_been_successfully_edited',
},
{ number: values.bill_number },
),
intent: Intent.SUCCESS,
});
setSubmitting(false);
@@ -242,10 +260,16 @@ function BillForm({
},
});
useEffect(() => {
formik.setFieldValue('bill_number', billNumber);
setBillNumberChanged(false);
}, [nextBillNumberChanged, billNumber]);
const handleSubmitClick = useCallback(
(payload) => {
setPayload(payload);
formik.submitForm();
formik.setSubmitting(false);
},
[setPayload, formik],
);
@@ -315,6 +339,7 @@ function BillForm({
formik={formik}
onSubmitClick={handleSubmitClick}
bill={bill}
disabled={formik.isSubmitting}
onCancelClick={handleCancelClick}
/>
</div>
@@ -323,7 +348,12 @@ function BillForm({
export default compose(
withBillActions,
withBillDetail(),
withBills(({ nextBillNumberChanged }) => ({ nextBillNumberChanged })),
withDashboardActions,
withMediaActions,
withBillDetail(),
withSettings(({ billsettings }) => ({
billNextNumber: billsettings?.next_number,
billNumberPrefix: billsettings?.number_prefix,
})),
)(BillForm);

View File

@@ -12,6 +12,7 @@ export default function BillFormFooter({
<div className={'form__floating-footer'}>
<Button
disabled={isSubmitting}
loading={isSubmitting}
intent={Intent.PRIMARY}
type="submit"
onClick={() => {
@@ -23,6 +24,7 @@ export default function BillFormFooter({
<Button
disabled={isSubmitting}
loading={isSubmitting}
intent={Intent.PRIMARY}
className={'ml1'}
name={'save'}

View File

@@ -14,16 +14,17 @@ import moment from 'moment';
import { momentFormatter, compose, tansformDateValue } from 'utils';
import classNames from 'classnames';
import {
AccountsSelectList,
ListSelect,
ErrorMessage,
FieldRequiredHint,
Hint,
Icon,
InputPrependButton,
} from 'components';
// import withCustomers from 'containers/Customers/withCustomers';
import withVendors from 'containers/Vendors/withVendors';
import withAccounts from 'containers/Accounts/withAccounts';
import withDialogActions from 'containers/Dialog/withDialogActions';
function BillFormHeader({
formik: { errors, touched, setFieldValue, getFieldProps, values },
@@ -33,6 +34,9 @@ function BillFormHeader({
vendorItems,
//#withAccouts
accountsList,
// #withDialog
openDialog,
}) {
const handleDateChange = useCallback(
(date_filed) => (date) => {
@@ -76,6 +80,10 @@ function BillFormHeader({
}
};
const handleBillNumberChange = useCallback(() => {
openDialog('bill-number-form', {});
}, [openDialog]);
return (
<div className="page-form page-form--bill">
<div className={'page-form__primary-section'}>
@@ -91,7 +99,7 @@ function BillFormHeader({
}
>
<ListSelect
items={vendorsCurrentPage}
items={vendorItems}
noResults={<MenuItem disabled={true} text="No results." />}
itemRenderer={vendorNameRenderer}
itemPredicate={filterVendorAccount}
@@ -146,7 +154,7 @@ function BillFormHeader({
<FormGroup
label={<T id={'bill_number'} />}
inline={true}
className={('form-group--estimate', Classes.FILL)}
className={('form-group--bill_number', Classes.FILL)}
labelInfo={<FieldRequiredHint />}
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
helperText={
@@ -156,6 +164,19 @@ function BillFormHeader({
<InputGroup
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
minimal={true}
rightElement={
<InputPrependButton
buttonProps={{
onClick: handleBillNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: 'Setting your auto-generated bill number',
position: Position.BOTTOM_LEFT,
}}
/>
}
{...getFieldProps('bill_number')}
/>
</FormGroup>
@@ -185,4 +206,5 @@ export default compose(
withAccounts(({ accountsList }) => ({
accountsList,
})),
withDialogActions,
)(BillFormHeader);

View File

@@ -9,6 +9,7 @@ import withVendorActions from 'containers/Vendors/withVendorActions';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withItemsActions from 'containers/Items/withItemsActions';
import withBillActions from './withBillActions';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import { compose } from 'utils';
@@ -24,6 +25,9 @@ function Bills({
//# withBilleActions
requestFetchBill,
// #withSettingsActions
requestFetchOptions,
}) {
const history = useHistory();
const { id } = useParams();
@@ -41,6 +45,8 @@ function Bills({
// Handle fetch Items data table or list
const fetchItems = useQuery('items-list', () => requestFetchItems({}));
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
const handleFormSubmit = useCallback(
(payload) => {
payload.redirect && history.push('/bills');
@@ -82,4 +88,5 @@ export default compose(
withVendorActions,
withItemsActions,
withAccountsActions,
withSettingsActions,
)(Bills);

View File

@@ -96,9 +96,13 @@ function BillsDataTable({
const actionMenuList = useCallback(
(bill) => (
<Menu>
<MenuItem text={formatMessage({ id: 'view_details' })} />
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_bill' })}
onClick={handleEditBill(bill)}
/>

View File

@@ -11,7 +11,7 @@ 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 )),
requestEditBill: (id, form) => dispatch(editBill(id, form)),
requestDeleteBill: (id) => dispatch(deleteBill({ id })),
requestFetchBillsTable: (query = {}) =>
dispatch(fetchBillsTable({ query: { ...query } })),
@@ -27,6 +27,11 @@ const mapDispatchToProps = (dispatch) => ({
type: t.BILLS_TABLE_QUERIES_ADD,
queries,
}),
setBillNumberChanged: (isChanged) =>
dispatch({
type: t.BILL_NUMBER_CHANGED,
payload: { isChanged },
}),
});
export default connect(null, mapDispatchToProps);

View File

@@ -20,9 +20,9 @@ export default (mapState) => {
billsItems: state.bills.items,
billsTableQuery: tableQuery,
// @todo un-unncessery shit.
billsPageination: getBillsPaginationMeta(state, props, tableQuery),
billsLoading: state.bills.loading,
nextBillNumberChanged: state.bills.nextBillNumberChanged,
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -5,9 +5,10 @@ export default (mapState) => {
const mapped = {
organizationSettings: state.settings.data.organization,
manualJournalsSettings: state.settings.data.manual_journals,
billsettings: state.settings.data.bills,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
}
};

View File

@@ -16,7 +16,7 @@ export default (mapState) => {
const mapped = {
vendorsCurrentPage: getVendorsItems(state, props, query),
vendorViews: getResourceViews(state, props, 'vendors'),
vendorItems: state.vendors.items,
vendorItems: Object.values(state.vendors.items),
vendorTableQuery: query,
vendorsPageination: getVendorsPaginationMeta(state, props, query),
vendorsLoading: state.vendors.loading,

View File

@@ -211,7 +211,7 @@ export default {
once_delete_this_item_you_will_able_to_restore_it: `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?<br /><br />If you're not sure, you can inactivate it instead.`,
the_item_has_been_successfully_deleted:
'The item has been successfully deleted.',
the_item_has_been_successfully_edited:
the_item_has_been_successfully_edited:
'The item #{number} has been successfully edited.',
the_item_category_has_been_successfully_created:
'The item category has been successfully created.',
@@ -777,4 +777,5 @@ export default {
prefix: 'Prefix',
next_number: 'Next Number',
journal_number_settings: 'Journal number settings',
bill_number_settings: 'Bill number settings',
};

View File

@@ -19,21 +19,21 @@ export const fetchBillsTable = ({ query = {} }) => {
dispatch({
type: t.BILLS_PAGE_SET,
payload: {
bills: response.data.bills.results,
pagination: response.data.bills.pagination,
bills: response.data.bills,
pagination: response.data.pagination,
customViewId: response.data.customViewId || -1,
},
});
dispatch({
type: t.BILLS_ITEMS_SET,
payload: {
bills: response.data.bills.results,
bills: response.data.bills,
},
});
dispatch({
type: t.BILLS_PAGINATION_SET,
payload: {
pagination: response.data.bills.pagination,
pagination: response.data.pagination,
customViewId: response.data.customViewId || -1,
},
});
@@ -66,7 +66,19 @@ export const deleteBill = ({ id }) => {
};
export const submitBill = ({ form }) => {
return (dispatch) => ApiService.post('purchases/bills', form);
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.post('purchases/bills', form)
.then((response) => {
resolve(response);
})
.catch((error) => {
const { response } = error;
const { data } = response;
reject(data?.errors);
});
});
};
export const fetchBill = ({ id }) => {
@@ -91,5 +103,17 @@ export const fetchBill = ({ id }) => {
};
export const editBill = (id, form) => {
return (dispatch) => ApiService.post(`purchases/bills/${id}`, form);
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.post(`purchases/bills/${id}`, form)
.then((response) => {
resolve(response);
})
.catch((error) => {
const { response } = error;
const { data } = response;
reject(data?.errors);
});
});
};

View File

@@ -12,6 +12,7 @@ const initialState = {
page_size: 5,
page: 1,
},
nextBillNumberChanged: false,
};
const defaultBill = {
@@ -98,6 +99,11 @@ const reducer = createReducer(initialState, {
},
};
},
[t.BILL_NUMBER_CHANGED]: (state, action) => {
const { isChanged } = action.payload;
state.nextBillNumberChanged = isChanged;
},
});
export default createTableQueryReducers('bills', reducer);

View File

@@ -9,4 +9,5 @@ export default {
BILLS_PAGINATION_SET: 'BILLS_PAGINATION_SET',
BILLS_PAGE_SET: 'BILLS_PAGE_SET',
BILLS_ITEMS_SET: 'BILLS_ITEMS_SET',
BILL_NUMBER_CHANGED: 'BILL_NUMBER_CHANGED',
};

View File

@@ -17,7 +17,7 @@ export const fetchVendorsTable = ({ query }) => {
payload: {
vendors: response.data.vendors,
pagination: response.data.pagination,
customViewId: response.data.customViewId,
customViewId: response.data.customViewId || -1,
},
});
dispatch({

View File

@@ -1,58 +1,66 @@
export default {
organization: [
{
key: 'name',
type: 'string',
key: "name",
type: "string",
config: true,
},
{
key: 'base_currency',
type: 'string',
key: "base_currency",
type: "string",
config: true,
},
{
key: 'industry',
type: 'string',
key: "industry",
type: "string",
},
{
key: 'location',
type: 'string',
key: "location",
type: "string",
},
{
key: 'fiscal_year',
type: 'string',
key: "fiscal_year",
type: "string",
// config: true,
},
{
key: 'financial_date_start',
type: 'string',
key: "financial_date_start",
type: "string",
},
{
key: 'language',
type: 'string',
key: "language",
type: "string",
config: true,
},
{
key: 'time_zone',
type: 'string',
{
key: "time_zone",
type: "string",
// config: true,
},
{
key: 'date_format',
type: 'string',
key: "date_format",
type: "string",
// config: true,
},
],
manual_journals: [
{
key: 'next_number',
type: 'number',
key: "next_number",
type: "number",
},
{
key: 'number_prefix',
type: 'string',
key: "number_prefix",
type: "string",
},
]
};
],
bills: [
{
key: "next_number",
type: "number",
},
{
key: "number_prefix",
type: "string",
},
],
};