fix: Filter financial reports by items, customers or vendors.

This commit is contained in:
a.bouhuolia
2021-07-25 03:59:02 +02:00
parent 504b380da6
commit 3a7f8a4512
71 changed files with 1021 additions and 350 deletions

View File

@@ -1,58 +1,57 @@
import React, { useMemo, useCallback, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { MenuItem, Button } from '@blueprintjs/core';
import { omit } from 'lodash';
import intl from 'react-intl-universal';
import MultiSelect from 'components/MultiSelect';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import { safeInvoke } from 'utils';
/**
* Contacts multi-select component.
*/
export default function ContactsMultiSelect({
contacts,
defaultText = <T id={'all_customers'} />,
buttonProps,
onCustomerSelected: onContactSelected,
...selectProps
onContactSelect,
contactsSelected = [],
...multiSelectProps
}) {
const [selectedContacts, setSelectedContacts] = useState({});
const [localSelected, setLocalSelected] = useState([ ...contactsSelected]);
const isContactSelect = useCallback(
(id) => typeof selectedContacts[id] !== 'undefined',
[selectedContacts],
// Detarmines the given id is selected.
const isItemSelected = useCallback(
(id) => localSelected.some(s => s === id),
[localSelected],
);
// Contact item renderer.
const contactRenderer = useCallback(
(contact, { handleClick }) => (
<MenuItem
icon={isContactSelect(contact.id) ? 'tick' : 'blank'}
icon={isItemSelected(contact.id) ? 'tick' : 'blank'}
text={contact.display_name}
key={contact.id}
onClick={handleClick}
/>
),
[isContactSelect],
[isItemSelected],
);
const countSelected = useMemo(
() => Object.values(selectedContacts).length,
[selectedContacts],
);
// Count selected items.
const countSelected = localSelected.length;
const onContactSelect = useCallback(
// Handle item selected.
const handleItemSelect = useCallback(
({ id }) => {
const selected = {
...(isContactSelect(id)
? {
...omit(selectedContacts, [id]),
}
: {
...selectedContacts,
[id]: true,
}),
};
setSelectedContacts({ ...selected });
onContactSelected && onContactSelected(selected);
const selected = isItemSelected(id)
? localSelected.filter(s => s !== id)
: [...localSelected, id];
setLocalSelected([ ...selected ]);
safeInvoke(onContactSelect, selected);
},
[setSelectedContacts, selectedContacts, isContactSelect, onContactSelected],
[setLocalSelected, localSelected, isItemSelected, onContactSelect],
);
return (
@@ -62,7 +61,8 @@ export default function ContactsMultiSelect({
itemRenderer={contactRenderer}
popoverProps={{ minimal: true }}
filterable={true}
onItemSelect={onContactSelect}
onItemSelect={handleItemSelect}
{...multiSelectProps}
>
<Button
text={

View File

@@ -1,6 +1,15 @@
import React from 'react';
import className from 'classnames';
import 'style/containers/FinancialStatements/FinancialSheet.scss';
export default function FinancialStatements({ children }) {
return <div class="financial-statement">{children}</div>;
export default function FinancialStatements({ name, children }) {
return (
<div
className={className('financial-statement', {
[`financial-statement--${name}`]: name,
})}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,77 @@
import React, { useCallback, useState } from 'react';
import { MenuItem, Button } from '@blueprintjs/core';
import intl from 'react-intl-universal';
import MultiSelect from 'components/MultiSelect';
import { FormattedMessage as T } from 'components';
import { safeInvoke } from 'utils';
/**
* Items multi-select.
*/
export function ItemsMultiSelect({
items,
defaultText = <T id={'All items'} />,
buttonProps,
selectedItems = [],
onItemSelect,
...multiSelectProps
}) {
const [localSelected, setLocalSelected] = useState([...selectedItems]);
// Detarmines the given id is selected.
const isItemSelected = useCallback(
(id) => localSelected.some((s) => s === id),
[localSelected],
);
// Contact item renderer.
const itemRenderer = useCallback(
(item, { handleClick }) => (
<MenuItem
icon={isItemSelected(item.id) ? 'tick' : 'blank'}
text={item.name}
key={item.id}
onClick={handleClick}
/>
),
[isItemSelected],
);
// Count selected items.
const countSelected = localSelected.length;
// Handle item selected.
const handleItemSelect = useCallback(
({ id }) => {
const selected = isItemSelected(id)
? localSelected.filter((s) => s !== id)
: [...localSelected, id];
setLocalSelected([...selected]);
safeInvoke(onItemSelect, selected);
},
[setLocalSelected, localSelected, isItemSelected, onItemSelect],
);
return (
<MultiSelect
items={items}
noResults={<MenuItem disabled={true} text={<T id={'No items'} />} />}
itemRenderer={itemRenderer}
popoverProps={{ minimal: true }}
filterable={true}
onItemSelect={handleItemSelect}
{...multiSelectProps}
>
<Button
text={
countSelected === 0
? defaultText
: intl.get('Selected items ({count})', { count: countSelected })
}
{...buttonProps}
/>
</MultiSelect>
);
}

View File

@@ -0,0 +1 @@
export * from './ItemsMultiSelect';

View File

@@ -13,18 +13,16 @@
* limitations under the License.
*/
import classNames from "classnames";
import * as React from "react";
import classNames from 'classnames';
import * as React from 'react';
import {
Classes,
DISPLAYNAME_PREFIX,
Popover,
Position,
Utils,
} from "@blueprintjs/core";
import {
QueryList,
} from '@blueprintjs/select';
} from '@blueprintjs/core';
import { QueryList } from '@blueprintjs/select';
// export interface IMultiSelectProps<T> extends IListItemsProps<T> {
// /**
@@ -63,114 +61,124 @@ import {
// }
export default class MultiSelect extends React.Component {
static get displayName() {
return `${DISPLAYNAME_PREFIX}.MultiSelect`;
}
static get displayName() {
return `${DISPLAYNAME_PREFIX}.MultiSelect`;
}
static get defaultProps() {
return {
fill: false,
placeholder: "Search...",
};
static get defaultProps() {
return {
fill: false,
placeholder: 'Search...',
};
}
constructor(props) {
super(props);
constructor(props) {
super(props);
this.state = { isOpen: (this.props.popoverProps && this.props.popoverProps.isOpen) || false };
this.input = null;
this.queryList = null;
this.listRef = React.createRef();
this.state = {
isOpen:
(this.props.popoverProps && this.props.popoverProps.isOpen) || false,
};
this.input = null;
this.queryList = null;
this.listRef = React.createRef();
this.refHandlers = {
queryList: (ref) => {
this.queryList = ref;
},
};
this.refHandlers = {
queryList: (ref) => {
this.queryList = ref;
},
};
}
render() {
// omit props specific to this component, spread the rest.
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } =
this.props;
return (
<QueryList
{...restProps}
onItemSelect={this.handleItemSelect.bind(this)}
onQueryChange={this.handleQueryChange.bind(this)}
ref={this.refHandlers.queryList}
renderer={this.renderQueryList.bind(this)}
/>
);
}
renderQueryList(listProps) {
const { fill, tagInputProps = {}, popoverProps = {} } = this.props;
const { handleKeyDown, handleKeyUp } = listProps;
if (fill) {
popoverProps.fill = true;
tagInputProps.fill = true;
}
render() {
// omit props specific to this component, spread the rest.
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } = this.props;
return (
<QueryList
{...restProps}
onItemSelect={this.handleItemSelect.bind(this)}
onQueryChange={this.handleQueryChange.bind(this)}
ref={this.refHandlers.queryList}
renderer={this.renderQueryList.bind(this)}
/>
);
}
// add our own inputProps.className so that we can reference it in event handlers
const { inputProps = {} } = tagInputProps;
inputProps.className = classNames(
inputProps.className,
Classes.MULTISELECT_TAG_INPUT_INPUT,
);
renderQueryList(listProps) {
const { fill, tagInputProps = {}, popoverProps = {} } = this.props;
const { handleKeyDown, handleKeyUp } = listProps;
if (fill) {
popoverProps.fill = true;
tagInputProps.fill = true;
}
// add our own inputProps.className so that we can reference it in event handlers
const { inputProps = {} } = tagInputProps;
inputProps.className = classNames(inputProps.className, Classes.MULTISELECT_TAG_INPUT_INPUT);
return (
<Popover
autoFocus={false}
canEscapeKeyClose={true}
enforceFocus={false}
isOpen={this.state.isOpen}
position={Position.BOTTOM_LEFT}
{...popoverProps}
className={classNames(listProps.className, popoverProps.className)}
onInteraction={this.handlePopoverInteraction.bind(this)}
popoverClassName={classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName)}
onOpened={this.handlePopoverOpened.bind(this)}
return (
<Popover
autoFocus={false}
canEscapeKeyClose={true}
enforceFocus={false}
isOpen={this.state.isOpen}
position={Position.BOTTOM_LEFT}
{...popoverProps}
className={classNames(listProps.className, popoverProps.className)}
onInteraction={this.handlePopoverInteraction.bind(this)}
popoverClassName={classNames(
Classes.MULTISELECT_POPOVER,
popoverProps.popoverClassName,
)}
onOpened={this.handlePopoverOpened.bind(this)}
>
<div
onKeyDown={
this.state.isOpen ? handleKeyDown : this.handleTargetKeyDown
}
onKeyUp={this.state.isOpen ? handleKeyUp : undefined}
>
<div
onKeyDown={this.state.isOpen ? handleKeyDown : this.handleTargetKeyDown}
onKeyUp={this.state.isOpen ? handleKeyUp : undefined}
>
{this.props.children}
</div>
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} ref={this.listRef}>
{listProps.itemList}
</div>
</Popover>
);
};
{this.props.children}
</div>
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} ref={this.listRef}>
{listProps.itemList}
</div>
</Popover>
);
}
handleItemSelect(item, evt) {
if (this.input != null) {
this.input.focus();
}
Utils.safeInvoke(this.props.onItemSelect, item, evt);
};
handleQueryChange(query, evt) {
this.setState({ isOpen: query.length > 0 || !this.props.openOnKeyDown });
Utils.safeInvoke(this.props.onQueryChange, query, evt);
};
handlePopoverInteraction = (isOpen, e) => {
if (e && this.listRef.current && this.listRef.current.contains(e.target)) {
this.setState({ isOpen: true })
} else {
this.setState({ isOpen });
}
Utils.safeInvokeMember(this.props.popoverProps, "onInteraction", isOpen);
handleItemSelect(item, evt) {
if (this.input != null) {
this.input.focus();
}
Utils.safeInvoke(this.props.onItemSelect, item, evt);
}
handlePopoverOpened(node) {
if (this.queryList != null) {
// scroll active item into view after popover transition completes and all dimensions are stable.
this.queryList.scrollActiveItemIntoView();
}
Utils.safeInvokeMember(this.props.popoverProps, "onOpened", node);
};
handleQueryChange(query, evt) {
this.setState({ isOpen: query.length > 0 || !this.props.openOnKeyDown });
Utils.safeInvoke(this.props.onQueryChange, query, evt);
}
handlePopoverInteraction = (isOpen, e) => {
if (e && this.listRef.current && this.listRef.current.contains(e.target)) {
this.setState({ isOpen: true });
} else {
this.setState({ isOpen });
}
Utils.safeInvokeMember(this.props.popoverProps, 'onInteraction', isOpen);
};
handlePopoverOpened(node) {
if (this.queryList != null) {
// scroll active item into view after popover transition completes and all dimensions are stable.
this.queryList.scrollActiveItemIntoView();
}
Utils.safeInvokeMember(this.props.popoverProps, 'onOpened', node);
}
}

View File

@@ -58,6 +58,8 @@ import AccountsSuggestField from './AccountsSuggestField';
import MaterialProgressBar from './MaterialProgressBar';
import { MoneyFieldCell } from './DataTableCells';
import { ItemsMultiSelect } from './Items';
const Hint = FieldHint;
const T = FormattedMessage;
@@ -125,4 +127,5 @@ export {
AccountsSuggestField,
MaterialProgressBar,
MoneyFieldCell,
ItemsMultiSelect
};

View File

@@ -30,6 +30,7 @@ function APAgingSummary({
asDate: moment().endOf('day').format('YYYY-MM-DD'),
agingBeforeDays: 30,
agingPeriods: 3,
vendorsIds: [],
});
// Handle filter submit.
@@ -63,7 +64,7 @@ function APAgingSummary({
<APAgingSummarySheetLoadingBar />
<DashboardPageContent>
<FinancialStatement>
<FinancialStatement name={'AP-aging-summary'}>
<APAgingSummaryHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -12,6 +12,7 @@ import withAPAgingSummary from './withAPAgingSummary';
import withAPAgingSummaryActions from './withAPAgingSummaryActions';
import { compose } from 'utils';
import { transformToForm } from '../../../utils';
/**
* AP Aging Summary Report - Drawer Header.
@@ -25,16 +26,17 @@ function APAgingSummaryHeader({
toggleAPAgingSummaryFilterDrawer: toggleFilterDrawerDisplay,
// #withAPAgingSummary
isFilterDrawerOpen
isFilterDrawerOpen,
}) {
// Validation schema.
const validationSchema = Yup.object({
as_date: Yup.date().required().label('asDate'),
aging_days_before: Yup.number()
asDate: Yup.date().required().label('asDate'),
agingDaysBefore: Yup.number()
.required()
.integer()
.positive()
.label('agingBeforeDays'),
aging_periods: Yup.number()
agingPeriods: Yup.number()
.required()
.integer()
.positive()
@@ -42,11 +44,14 @@ function APAgingSummaryHeader({
});
// Initial values.
const initialValues = {
as_date: moment(pageFilter.asDate).toDate(),
aging_days_before: 30,
aging_periods: 3,
const defaultValues = {
asDate: moment(pageFilter.asDate).toDate(),
agingDaysBefore: 30,
agingPeriods: 3,
vendorsIds: [],
};
// Formik initial values.
const initialValues = transformToForm(pageFilter, defaultValues);
// Handle form submit.
const handleSubmit = (values, { setSubmitting }) => {
@@ -55,18 +60,17 @@ function APAgingSummaryHeader({
setSubmitting(false);
};
// handle cancel button click.
const handleCancelClick = () => {
toggleFilterDrawerDisplay(false);
};
// Handle cancel button click.
const handleCancelClick = () => { toggleFilterDrawerDisplay(false); };
// Handle the drawer closing.
const handleDrawerClose = () => {
toggleFilterDrawerDisplay(false);
};
const handleDrawerClose = () => { toggleFilterDrawerDisplay(false); };
return (
<FinancialStatementHeader isOpen={isFilterDrawerOpen} drawerProps={{ onClose: handleDrawerClose }}>
<FinancialStatementHeader
isOpen={isFilterDrawerOpen}
drawerProps={{ onClose: handleDrawerClose }}
>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { FastField } from 'formik';
import { FastField, Field } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import {
Intent,
@@ -51,9 +51,10 @@ export default function APAgingSummaryHeaderGeneral() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<FastField name={'aging_days_before'}>
<FastField name={'agingDaysBefore'}>
{({ field, meta: { error } }) => (
<FormGroup
label={<T id={'aging_before_days'} />}
@@ -66,9 +67,10 @@ export default function APAgingSummaryHeaderGeneral() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<FastField name={'aging_periods'}>
<FastField name={'agingPeriods'}>
{({ field, meta: { error } }) => (
<FormGroup
label={<T id={'aging_periods'} />}
@@ -81,17 +83,29 @@ export default function APAgingSummaryHeaderGeneral() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<FormGroup
label={<T id={'specific_vendors'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
defaultText={<T id={'all_vendors'} />}
contacts={vendors}
/>
</FormGroup>
<Field name={'vendorsIds'}>
{({
form: { setFieldValue },
field: { value },
}) => (
<FormGroup
label={<T id={'specific_vendors'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
defaultText={<T id={'all_vendors'} />}
contacts={vendors}
contactsSelected={value}
onContactSelect={(contactsIds) => {
setFieldValue('vendorsIds', contactsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>

View File

@@ -18,7 +18,7 @@ import withSettings from 'containers/Settings/withSettings';
import { compose } from 'utils';
/**
* AR aging summary report.
* A/R aging summary report.
*/
function ReceivableAgingSummarySheet({
// #withSettings
@@ -31,6 +31,7 @@ function ReceivableAgingSummarySheet({
asDate: moment().endOf('day').format('YYYY-MM-DD'),
agingDaysBefore: 30,
agingPeriods: 3,
customersIds: [],
});
// Handle filter submit.
@@ -61,7 +62,7 @@ function ReceivableAgingSummarySheet({
<ARAgingSummarySheetLoadingBar />
<DashboardPageContent>
<FinancialStatement>
<FinancialStatement name={'AR-aging-summary'}>
<ARAgingSummaryHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -11,7 +11,7 @@ import ARAgingSummaryHeaderGeneral from './ARAgingSummaryHeaderGeneral';
import withARAgingSummary from './withARAgingSummary';
import withARAgingSummaryActions from './withARAgingSummaryActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* AR Aging Summary Report - Drawer Header.
@@ -41,11 +41,20 @@ function ARAgingSummaryHeader({
.label('agingPeriods'),
});
// Initial values.
const initialValues = {
asDate: moment(pageFilter.asDate).toDate(),
const defaultValues = {
asDate: moment().toDate(),
agingDaysBefore: 30,
agingPeriods: 3,
customersIds: [],
};
// Initial values.
const initialValues = transformToForm(
{
...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
},
defaultValues,
);
// Handle form submit.
const handleSubmit = (values, { setSubmitting }) => {
@@ -58,7 +67,7 @@ function ARAgingSummaryHeader({
const handleCancelClick = () => {
toggleFilterDrawerDisplay(false);
};
// Handle the drawer close.
const handleDrawerClose = () => {
toggleFilterDrawerDisplay(false);

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { FastField } from 'formik';
import { FastField, Field } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import {
Intent,
@@ -93,14 +93,24 @@ export default function ARAgingSummaryHeaderGeneral() {
</Row>
<Row>
<Col xs={5}>
<FormGroup
label={<T id={'specific_customers'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect contacts={customers} />
</FormGroup>
<Field name="customersIds">
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'specific_customers'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
contacts={customers}
contactsSelected={value}
onContactSelect={(contactsIds) => {
setFieldValue('customersIds', contactsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}
}

View File

@@ -11,7 +11,7 @@ import FinancialStatementHeader from 'containers/FinancialStatements/FinancialSt
import withBalanceSheet from './withBalanceSheet';
import withBalanceSheetActions from './withBalanceSheetActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
import BalanceSheetHeaderGeneralPanal from './BalanceSheetHeaderGeneralPanal';
/**
@@ -28,20 +28,25 @@ function BalanceSheetHeader({
// #withBalanceSheetActions
toggleBalanceSheetFilterDrawer: toggleFilterDrawer,
}) {
// Filter form initial values.
const initialValues = {
basis: 'cash',
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
const defaultValues = {
basic: 'cash',
fromDate: moment().toDate(),
toDate: moment().toDate(),
};
// Filter form initial values.
const initialValues = transformToForm(
{
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Validation schema.
const validationSchema = Yup.object().shape({
dateRange: Yup.string().optional(),
fromDate: Yup.date()
.required()
.label(intl.get('fromDate')),
fromDate: Yup.date().required().label(intl.get('fromDate')),
toDate: Yup.date()
.min(Yup.ref('fromDate'))
.required()
@@ -58,14 +63,10 @@ function BalanceSheetHeader({
};
// Handle cancel button click.
const handleCancelClick = () => {
toggleFilterDrawer(false);
};
const handleCancelClick = () => { toggleFilterDrawer(false); };
// Handle drawer close action.
const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
const handleDrawerClose = () => { toggleFilterDrawer(false); };
return (
<FinancialStatementHeader

View File

@@ -12,7 +12,7 @@ import CashFlowStatementGeneralPanel from './CashFlowStatementGeneralPanel';
import withCashFlowStatement from './withCashFlowStatement';
import withCashFlowStatementActions from './withCashFlowStatementActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Cash flow statement header.
@@ -22,18 +22,24 @@ function CashFlowStatementHeader({
onSubmitFilter,
pageFilter,
//#withCashFlowStatement
// #withCashFlowStatement
isFilterDrawerOpen,
//#withCashStatementActions
// #withCashStatementActions
toggleCashFlowStatementFilterDrawer,
}) {
// filter form initial values.
const initialValues = {
// Filter form default values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
};
// Initial form values.
const initialValues = transformToForm({
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
};
}, defaultValues);
// Validation schema.
const validationSchema = Yup.object().shape({

View File

@@ -1,14 +1,9 @@
import React from 'react';
import { FastField } from 'formik';
import { FastField, Field } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import {
FormGroup,
Position,
Classes,
Checkbox,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import { Classes, FormGroup, Position, Checkbox } from '@blueprintjs/core';
import { ContactsMultiSelect, FormattedMessage as T } from 'components';
import classNames from 'classnames';
import { Row, Col, FieldHint } from 'components';
import {
momentFormatter,
@@ -16,11 +11,14 @@ import {
inputIntent,
handleDateChange,
} from 'utils';
import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider';
/**
* Customers balance header - general panel.
*/
export default function CustomersBalanceSummaryGeneralPanel() {
const { customers } = useCustomersBalanceSummaryContext();
return (
<div>
<Row>
@@ -48,6 +46,7 @@ export default function CustomersBalanceSummaryGeneralPanel() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<FastField name={'percentage'} type={'checkbox'}>
@@ -57,7 +56,7 @@ export default function CustomersBalanceSummaryGeneralPanel() {
inline={true}
name={'percentage'}
small={true}
label={<T id={'percentage_of_column'}/>}
label={<T id={'percentage_of_column'} />}
{...field}
/>
</FormGroup>
@@ -65,6 +64,31 @@ export default function CustomersBalanceSummaryGeneralPanel() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<Field name={'customersIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'Specific customers'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
onContactSelect={(contactsIds) => {
setFieldValue('customersIds', contactsIds);
}}
contacts={customers}
contactsSelected={value}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -4,14 +4,13 @@ import { Formik, Form } from 'formik';
import moment from 'moment';
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
import withCustomersBalanceSummary from './withCustomersBalanceSummary';
import withCustomersBalanceSummaryActions from './withCustomersBalanceSummaryActions';
import CustomersBalanceSummaryGeneralPanel from './CustomersBalanceSummaryGeneralPanel';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Customers balance summary.
@@ -33,11 +32,17 @@ function CustomersBalanceSummaryHeader({
asDate: Yup.date().required().label('asDate'),
});
// filter form initial values.
const initialValues = {
// Default form values.
const defaultValues = {
asDate: moment().toDate(),
customersIds: [],
};
// Filter form initial values.
const initialValues = transformToForm({
...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
};
}, defaultValues);
// handle form submit.
const handleSubmit = (values, { setSubmitting }) => {

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useCustomerBalanceSummaryReport } from 'hooks/query';
import { useCustomerBalanceSummaryReport, useCustomers } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const CustomersBalanceSummaryContext = createContext();
@@ -14,6 +14,7 @@ function CustomersBalanceSummaryProvider({ filter, ...props }) {
filter,
]);
// Fetches customers balance summary report based on the given report.
const {
data: CustomerBalanceSummary,
isLoading: isCustomersBalanceLoading,
@@ -23,10 +24,22 @@ function CustomersBalanceSummaryProvider({ filter, ...props }) {
keepPreviousData: true,
});
// Fetches the customers list.
const {
data: { customers },
isFetching: isCustomersFetching,
isLoading: isCustomersLoading,
} = useCustomers();
const provider = {
CustomerBalanceSummary,
isCustomersBalanceFetching,
isCustomersBalanceLoading,
isCustomersLoading,
isCustomersFetching,
customers,
refetch,
};
return (

View File

@@ -12,7 +12,7 @@ import CustomersTransactionsHeaderGeneralPanel from './CustomersTransactionsHead
import withCustomersTransactions from './withCustomersTransactions';
import withCustomersTransactionsActions from './withCustomersTransactionsActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Customers transactions header.
@@ -28,14 +28,18 @@ function CustomersTransactionsHeader({
//#withCustomersTransactionsActions
toggleCustomersTransactionsFilterDrawer: toggleFilterDrawer,
}) {
// Filter form initial values.
const initialValues = {
// Default form values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
customersIds: [],
};
// Initial form values.
const initialValues = transformToForm({
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
};
}, defaultValues);
// Validation schema.
const validationSchema = Yup.object().shape({
@@ -54,11 +58,8 @@ function CustomersTransactionsHeader({
toggleFilterDrawer(false);
setSubmitting(false);
};
// Handle drawer close action.
const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
const handleDrawerClose = () => { toggleFilterDrawer(false); };
return (
<FinancialStatementHeader

View File

@@ -1,13 +1,45 @@
import React from 'react';
import classNames from 'classnames';
import { Field } from 'formik';
import { Classes, FormGroup } from '@blueprintjs/core';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import { Row, Col } from 'components';
import { ContactsMultiSelect, FormattedMessage as T } from 'components';
import { useCustomersTransactionsContext } from './CustomersTransactionsProvider';
/**
* Customers transactions header - General panel.
*/
export default function CustomersTransactionsHeaderGeneralPanel() {
const { customers } = useCustomersTransactionsContext();
return (
<div>
<FinancialStatementDateRange />
<Row>
<Col xs={5}>
<Field name={'customersIds'}>
{({
form: { setFieldValue },
field: { value },
}) => (
<FormGroup
label={<T id={'specific_customers'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
onContactSelect={(contactsIds) => {
setFieldValue('customersIds', contactsIds);
}}
contacts={customers}
contactsSelected={value}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext, useMemo } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useCustomersTransactionsReport } from 'hooks/query';
import { useCustomersTransactionsReport, useCustomers } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const CustomersTransactionsContext = createContext();
@@ -21,11 +21,23 @@ function CustomersTransactionsProvider({ filter, ...props }) {
refetch: CustomersTransactionsRefetch,
} = useCustomersTransactionsReport(query, { keepPreviousData: true });
// Fetches the customers list.
const {
data: { customers },
isFetching: isCustomersFetching,
isLoading: isCustomersLoading,
} = useCustomers();
const provider = {
customersTransactions,
isCustomersTransactionsFetching,
isCustomersTransactionsLoading,
CustomersTransactionsRefetch,
customers,
isCustomersLoading,
isCustomersFetching,
filter,
query
};

View File

@@ -11,7 +11,7 @@ import GeneralLedgerHeaderGeneralPane from './GeneralLedgerHeaderGeneralPane';
import withGeneralLedger from './withGeneralLedger';
import withGeneralLedgerActions from './withGeneralLedgerActions';
import { compose, saveInvoke } from 'utils';
import { compose, transformToForm, saveInvoke } from 'utils';
/**
* Geenral Ledger (GL) - Header.
@@ -27,13 +27,22 @@ function GeneralLedgerHeader({
// #withGeneralLedger
isFilterDrawerOpen,
}) {
// Initial values.
const initialValues = {
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
// Default values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
};
// Initial values.
const initialValues = transformToForm(
{
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Validation schema.
const validationSchema = Yup.object().shape({
dateRange: Yup.string().optional(),

View File

@@ -33,7 +33,7 @@ function InventoryItemDetails({
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'),
});
// Handle filter submit.
const handleFilterSubmit = (filter) => {
const _filter = {
...filter,
@@ -61,10 +61,14 @@ function InventoryItemDetails({
/>
<InventoryItemDetailsLoadingBar />
<InventoryItemDetailsAlerts />
<DashboardPageContent>
<FinancialStatement>
<div className={'financial-statement--inventory-details'}>
<div
className={
'financial-statement financial-statement--inventory-details'
}
>
<InventoryItemDetailsHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -12,7 +12,7 @@ import InventoryItemDetailsHeaderGeneralPanel from './InventoryItemDetailsHeader
import withInventoryItemDetails from './withInventoryItemDetails';
import withInventoryItemDetailsActions from './withInventoryItemDetailsActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Inventory item details header.
@@ -21,20 +21,25 @@ function InventoryItemDetailsHeader({
// #ownProps
onSubmitFilter,
pageFilter,
//#withInventoryItemDetails
// #withInventoryItemDetails
isFilterDrawerOpen,
//#withInventoryItemDetailsActions
// #withInventoryItemDetailsActions
toggleInventoryItemDetailsFilterDrawer: toggleFilterDrawer,
}) {
// Default form values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
itemsIds: [],
};
//Filter form initial values.
const initialValues = {
// Filter form initial values.
const initialValues = transformToForm({
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
};
}, defaultValues);
// Validation schema.
const validationSchema = Yup.object().shape({
@@ -56,9 +61,7 @@ function InventoryItemDetailsHeader({
};
// Handle drawer close action.
const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
const handleDrawerClose = () => { toggleFilterDrawer(false); };
return (
<FinancialStatementHeader

View File

@@ -1,13 +1,46 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, Classes } from '@blueprintjs/core';
import { Field } from 'formik';
import { Row, Col, FormattedMessage as T } from 'components';
import { ItemsMultiSelect } from 'components';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import { useInventoryItemDetailsContext } from './InventoryItemDetailsProvider';
/**
* Inventory item details header - General panel.
*/
export default function InventoryItemDetailsHeaderGeneralPanel() {
const { items } = useInventoryItemDetailsContext();
return (
<div>
<FinancialStatementDateRange />
<Row>
<Col xs={4}>
<Field name={'itemsIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'Specific items'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ItemsMultiSelect
items={items}
selectedItems={value}
onItemSelect={(itemsIds) => {
setFieldValue('itemsIds', itemsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useInventoryItemDetailsReport } from 'hooks/query';
import { useItems, useInventoryItemDetailsReport } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const InventoryItemDetailsContext = React.createContext();
@@ -14,7 +14,7 @@ function InventoryItemDetailsProvider({ filter, ...props }) {
[filter],
);
// fetch inventory item details.
// Fetching inventory item details report based on the givne query.
const {
data: inventoryItemDetails,
isFetching: isInventoryItemDetailsFetching,
@@ -22,11 +22,23 @@ function InventoryItemDetailsProvider({ filter, ...props }) {
refetch: inventoryItemDetailsRefetch,
} = useInventoryItemDetailsReport(query, { keepPreviousData: true });
// Handle fetching the items based on the given query.
const {
data: { items },
isLoading: isItemsLoading,
isFetching: isItemsFetching,
} = useItems({ page_size: 10000 });
const provider = {
inventoryItemDetails,
isInventoryItemDetailsFetching,
isInventoryItemDetailsLoading,
inventoryItemDetailsRefetch,
isItemsFetching,
isItemsLoading,
items,
query,
filter,
};

View File

@@ -64,7 +64,7 @@ function InventoryValuation({
<InventoryValuationLoadingBar />
<DashboardPageContent>
<div class="financial-statement">
<div class="financial-statement financial-statement--inventory-valuation">
<InventoryValuationHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -2,7 +2,6 @@ import React from 'react';
import * as Yup from 'yup';
import moment from 'moment';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import { Formik, Form } from 'formik';
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
@@ -11,7 +10,7 @@ import InventoryValuationHeaderGeneralPanel from './InventoryValuationHeaderGene
import withInventoryValuation from './withInventoryValuation';
import withInventoryValuationActions from './withInventoryValuationActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* inventory valuation header.
@@ -27,18 +26,23 @@ function InventoryValuationHeader({
// #withInventoryValuationActions
toggleInventoryValuationFilterDrawer,
}) {
// Form validation schema.
const validationSchema = Yup.object().shape({
as_date: Yup.date().required().label('asDate'),
asDate: Yup.date().required().label('asDate'),
});
// Initial values.
const initialValues = {
as_date: moment(pageFilter.asDate).toDate(),
// Default values.
const defaultValues = {
asDate: moment().toDate(),
itemsIds: [],
};
// Initial values.
const initialValues = transformToForm({
...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
}, defaultValues);
// Handle the form of header submit.
const handleSubmit = (values, { setSubmitting }) => {
onSubmitFilter(values);
toggleInventoryValuationFilterDrawer(false);

View File

@@ -1,20 +1,24 @@
import React from 'react';
import { FastField } from 'formik';
import { FastField, Field } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import { FormGroup, Position } from '@blueprintjs/core';
import { FormGroup, Position, Classes } from '@blueprintjs/core';
import classNames from 'classnames';
import { FormattedMessage as T } from 'components';
import { Row, Col, FieldHint } from 'components';
import { ItemsMultiSelect, Row, Col, FieldHint } from 'components';
import {
momentFormatter,
tansformDateValue,
inputIntent,
handleDateChange,
} from 'utils';
import { useInventoryValuationContext } from './InventoryValuationProvider';
/**
* inventory valuation - Drawer Header - General panel.
* Inventory valuation - Drawer Header - General panel.
*/
export default function InventoryValuationHeaderGeneralPanel() {
const { items } = useInventoryValuationContext();
return (
<div>
<Row>
@@ -42,6 +46,31 @@ export default function InventoryValuationHeaderGeneralPanel() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<Field name={'itemsIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'Specific items'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ItemsMultiSelect
items={items}
selectedItems={value}
onItemSelect={(itemsIds) => {
setFieldValue('itemsIds', itemsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useInventoryValuation } from 'hooks/query';
import { useInventoryValuation, useItems } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const InventoryValuationContext = React.createContext();
@@ -20,11 +20,23 @@ function InventoryValuationProvider({ query, ...props }) {
},
);
// Handle fetching the items based on the given query.
const {
data: { items },
isLoading: isItemsLoading,
isFetching: isItemsFetching,
} = useItems({ page_size: 10000 });
// Provider data.
const provider = {
inventoryValuation,
isLoading,
isFetching,
refetchSheet: refetch,
items,
isItemsFetching,
isItemsLoading
};
return (

View File

@@ -66,7 +66,7 @@ function PurchasesByItems({
/>
<PurchasesByItemsLoadingBar />
<DashboardPageContent>
<div className="financial-statement">
<div className="financial-statement financial-statement--purchases-by-items">
<PurchasesByItemsHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -1,13 +1,47 @@
import React from 'react';
import { FormGroup, Classes } from '@blueprintjs/core';
import { Field } from 'formik';
import { Row, Col, FormattedMessage as T } from 'components';
import classNames from 'classnames';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import { ItemsMultiSelect } from 'components';
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
/**
* Purchases by items - Drawer header - General panel.
*/
export default function PurchasesByItemsGeneralPanel() {
const { items } = usePurchaseByItemsContext();
return (
<div>
<FinancialStatementDateRange />
<Row>
<Col xs={4}>
<Field name={'itemsIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'Specific items'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ItemsMultiSelect
items={items}
selectedItems={value}
onItemSelect={(itemsIds) => {
setFieldValue('itemsIds', itemsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -12,7 +12,7 @@ import PurchasesByItemsGeneralPanel from './PurchasesByItemsGeneralPanel';
import withPurchasesByItems from './withPurchasesByItems';
import withPurchasesByItemsActions from './withPurchasesByItemsActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Purchases by items header.
@@ -28,25 +28,31 @@ function PurchasesByItemsHeader({
// #withPurchasesByItems
togglePurchasesByItemsFilterDrawer,
}) {
// Form validation schema.
const validationSchema = Yup.object().shape({
fromDate: Yup.date()
.required()
.label(intl.get('from_date')),
fromDate: Yup.date().required().label(intl.get('from_date')),
toDate: Yup.date()
.min(Yup.ref('fromDate'))
.required()
.label(intl.get('to_date')),
});
// Initial values.
const initialValues = {
// Default form values.
const defaultValues = {
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
fromDate: moment().toDate(),
toDate: moment().toDate(),
itemsIds: [],
};
// Initial form values.
const initialValues = transformToForm(
{
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Handle form submit.
const handleSubmit = (values, { setSubmitting }) => {

View File

@@ -1,12 +1,13 @@
import React, { createContext, useContext } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { usePurchasesByItems } from 'hooks/query';
import { usePurchasesByItems, useItems } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const PurchasesByItemsContext = createContext();
function PurchasesByItemsProvider({ query, ...props }) {
// Handle fetching the purchases by items report based on the given query.
const {
data: purchaseByItems,
isFetching,
@@ -21,11 +22,23 @@ function PurchasesByItemsProvider({ query, ...props }) {
},
);
// Handle fetching the items based on the given query.
const {
data: { items },
isLoading: isItemsLoading,
isFetching: isItemsFetching,
} = useItems({ page_size: 10000 });
const provider = {
purchaseByItems,
isFetching,
isLoading,
refetchSheet: refetch,
items,
isItemsLoading,
isItemsFetching,
refetchSheet: refetch,
};
return (
<FinancialReportPage name={'purchase-by-items'}>

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useSalesByItems } from 'hooks/query';
import { useSalesByItems, useItems } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const SalesByItemsContext = createContext();
@@ -20,10 +20,22 @@ function SalesByItemProvider({ query, ...props }) {
},
);
// Handle fetching the items based on the given query.
const {
data: { items },
isLoading: isItemsLoading,
isFetching: isItemsFetching,
} = useItems({ page_size: 10000 });
const provider = {
salesByItems,
isFetching,
isLoading,
items,
isItemsLoading,
isItemsFetching,
refetchSheet: refetch,
};
return (

View File

@@ -68,7 +68,7 @@ function SalesByItems({
/>
<SalesByItemsLoadingBar />
<DashboardPageContent>
<div class="financial-statement">
<div class="financial-statement financial-statement--sales-by-items">
<SalesByItemsHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit}

View File

@@ -1,13 +1,45 @@
import React from 'react';
import { FormGroup, Classes } from '@blueprintjs/core';
import { Field } from 'formik';
import classNames from 'classnames';
import { Row, Col, ItemsMultiSelect, FormattedMessage as T } from 'components';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import { useSalesByItemsContext } from './SalesByItemProvider';
/**
* sells by items - Drawer header - General panel.
*/
export default function SalesByItemsHeaderGeneralPanel() {
const { items } = useSalesByItemsContext();
return (
<div>
<FinancialStatementDateRange />
<Row>
<Col xs={4}>
<Field name={'itemsIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'Specific items'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ItemsMultiSelect
items={items}
selectedItems={value}
onItemSelect={(itemsIds) => {
setFieldValue('itemsIds', itemsIds);
}}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -12,7 +12,7 @@ import TrialBalanceSheetHeaderGeneralPanel from './TrialBalanceSheetHeaderGenera
import withTrialBalance from './withTrialBalance';
import withTrialBalanceActions from './withTrialBalanceActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Trial balance sheet header.
@@ -28,42 +28,41 @@ function TrialBalanceSheetHeader({
// #withTrialBalanceActions
toggleTrialBalanceFilterDrawer: toggleFilterDrawer,
}) {
// Form validation schema.
const validationSchema = Yup.object().shape({
fromDate: Yup.date()
.required()
.label(intl.get('from_date')),
fromDate: Yup.date().required().label(intl.get('from_date')),
toDate: Yup.date()
.min(Yup.ref('fromDate'))
.required()
.label(intl.get('to_date')),
});
// Initial values.
const initialValues = {
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
// Default values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
};
// Initial values.
const initialValues = transformToForm(
{
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Handle form submit.
const handleSubmit = (values, { setSubmitting }) => {
onSubmitFilter(values);
setSubmitting(false);
toggleFilterDrawer(false);
};
// Handle drawer close action.
const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
const handleDrawerClose = () => { toggleFilterDrawer(false); };
// Handle cancel button click.
const handleCancelClick = () => {
toggleFilterDrawer(false);
};
const handleCancelClick = () => { toggleFilterDrawer(false); };
return (
<FinancialStatementHeader

View File

@@ -10,7 +10,7 @@ import withVendorsBalanceSummary from './withVendorsBalanceSummary';
import withVendorsBalanceSummaryActions from './withVendorsBalanceSummaryActions';
import VendorsBalanceSummaryHeaderGeneral from './VendorsBalanceSummaryHeaderGeneral';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Vendors balance summary drawer header.
@@ -32,10 +32,15 @@ function VendorsBalanceSummaryHeader({
});
// filter form initial values.
const initialValues = {
const defaultValues = {
asDate: moment().toDate(),
vendorsIds: [],
};
// Initial form values.
const initialValues = transformToForm({
...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
};
}, defaultValues);
// handle form submit.
const handleSubmit = (values, { setSubmitting }) => {

View File

@@ -1,8 +1,9 @@
import React from 'react';
import { FastField } from 'formik';
import { Field, FastField } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import classNames from 'classnames';
import { FormGroup, Position, Classes, Checkbox } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import { ContactsMultiSelect, FormattedMessage as T } from 'components';
import { Row, Col, FieldHint } from 'components';
import {
momentFormatter,
@@ -10,11 +11,14 @@ import {
inputIntent,
handleDateChange,
} from 'utils';
import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
/**
* Vendors balance header -general panel.
*/
export default function VendorsBalanceSummaryHeaderGeneral() {
const { vendors } = useVendorsBalanceSummaryContext();
return (
<div>
<Row>
@@ -42,6 +46,7 @@ export default function VendorsBalanceSummaryHeaderGeneral() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={5}>
<FastField name={'percentage'} type={'checkbox'}>
@@ -59,6 +64,31 @@ export default function VendorsBalanceSummaryHeaderGeneral() {
</FastField>
</Col>
</Row>
<Row>
<Col xs={4}>
<Field name={'vendorsIds'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'specific_vendors'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
onContactSelect={(contactsIds) => {
setFieldValue('vendorsIds', contactsIds);
}}
contacts={vendors}
contactsSelected={value}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useVendorsBalanceSummaryReport } from 'hooks/query';
import { useVendorsBalanceSummaryReport, useVendors } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const VendorsBalanceSummaryContext = React.createContext();
@@ -13,6 +13,7 @@ function VendorsBalanceSummaryProvider({ filter, ...props }) {
filter,
]);
// Fetching vendors balance summary report based on the given query.
const {
data: VendorBalanceSummary,
isLoading: isVendorsBalanceLoading,
@@ -22,10 +23,23 @@ function VendorsBalanceSummaryProvider({ filter, ...props }) {
keepPreviousData: true,
});
// Fetch vendors list with pagination meta.
const {
data: { vendors },
isLoading: isVendorsLoading,
isFetching: isVendorsFetching,
} = useVendors({ page_size: 1000000 });
// Provider.
const provider = {
VendorBalanceSummary,
isVendorsBalanceLoading,
isVendorsBalanceFetching,
vendors,
isVendorsFetching,
isVendorsLoading,
refetch,
};

View File

@@ -12,7 +12,7 @@ import VendorsTransactionsHeaderGeneralPanel from './VendorsTransactionsHeaderGe
import withVendorsTransaction from './withVendorsTransaction';
import withVendorsTransactionsActions from './withVendorsTransactionsActions';
import { compose } from 'utils';
import { compose, transformToForm } from 'utils';
/**
* Vendors transactions header.
@@ -29,14 +29,19 @@ function VendorsTransactionsHeader({
//#withVendorsTransactionsActions
toggleVendorsTransactionsFilterDrawer: toggleFilterDrawer,
}) {
// Default form values.
const defaultValues = {
fromDate: moment().toDate(),
toDate: moment().toDate(),
vendorsIds: [],
};
// Filter form initial values.
const initialValues = {
// Initial form values.
const initialValues = transformToForm({
...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
};
}, defaultValues);
// Validation schema.
const validationSchema = Yup.object().shape({
@@ -57,9 +62,7 @@ function VendorsTransactionsHeader({
};
// Handle drawer close action.
const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
const handleDrawerClose = () => { toggleFilterDrawer(false); };
return (
<FinancialStatementHeader

View File

@@ -1,13 +1,42 @@
import React from 'react';
import { Field } from 'formik';
import classNames from 'classnames';
import { Classes, FormGroup } from '@blueprintjs/core';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import { Row, Col } from 'components';
import { ContactsMultiSelect, FormattedMessage as T } from 'components';
import { useVendorsTransactionsContext } from './VendorsTransactionsProvider';
/**
* Vendors transactions header - General panel.
*/
export default function VendorsTransactionsHeaderGeneralPanel() {
const { vendors } = useVendorsTransactionsContext();
return (
<div>
<FinancialStatementDateRange />
<Row>
<Col xs={5}>
<Field name={'vendorsIds'}>
{({ form: { setFieldValue }, field: { value } }) => (
<FormGroup
label={<T id={'specific_vendors'} />}
className={classNames('form-group--select-list', Classes.FILL)}
>
<ContactsMultiSelect
onContactSelect={(contactsIds) => {
setFieldValue('vendorsIds', contactsIds);
}}
contacts={vendors}
contactsSelected={value}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext, useMemo } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useVendorsTransactionsReport } from 'hooks/query';
import { useVendorsTransactionsReport, useVendors } from 'hooks/query';
import { transformFilterFormToQuery } from '../common';
const VendorsTransactionsContext = createContext();
@@ -11,6 +11,7 @@ const VendorsTransactionsContext = createContext();
function VendorsTransactionsProvider({ filter, ...props }) {
const query = useMemo(() => transformFilterFormToQuery(filter), [filter]);
// Fetch vendors transactions based on the given query.
const {
data: vendorsTransactions,
isFetching: isVendorsTransactionFetching,
@@ -18,10 +19,22 @@ function VendorsTransactionsProvider({ filter, ...props }) {
refetch,
} = useVendorsTransactionsReport(query, { keepPreviousData: true });
// Fetch vendors list based on the given query.
const {
data: { vendors },
isLoading: isVendorsLoading,
isFetching: isVendorsFetching,
} = useVendors({ page_size: 100000 });
const provider = {
vendorsTransactions,
isVendorsTransactionsLoading,
isVendorsTransactionFetching,
vendors,
isVendorsLoading,
isVendorsFetching,
refetch,
filter,
query,

View File

@@ -82,5 +82,5 @@ export const transformFilterFormToQuery = (form) => {
noneZero: form.accountsFilter === 'without-zero-balance',
noneTransactions: form.accountsFilter === 'with-transactions',
});
return flatObject(transformed);
return transformed;
};

View File

@@ -1136,5 +1136,12 @@
"select_transaction":"Select transaction account",
"details":"Details",
"located_landed_cost":"Located Landed Cost",
"delete_transaction":"Delete transaction"
"delete_transaction":"Delete transaction",
"all_items": "All items",
"Specific items": "Specific items",
"Selected contacts": "Selected contacts",
"All contacts": "All contacts",
"Selected items ({count})": "Selected items ({count})",
"All items": "All items",
"No items": "No items"
}

View File

@@ -36,4 +36,13 @@
}
}
}
}
.financial-statement--AR-aging-summary{
.financial-header-drawer{
.bp3-drawer{
max-height: 450px;
}
}
}

View File

@@ -27,3 +27,23 @@
}
}
}
.financial-statement--sales-by-items,
.financial-statement--purchases-by-items{
.financial-header-drawer{
.bp3-drawer{
max-height: 400px;
}
}
}
.financial-statement--inventory-valuation{
.financial-header-drawer{
.bp3-drawer{
max-height: 350px;
}
}
}

View File

@@ -297,6 +297,10 @@ export const saveInvoke = (func, ...rest) => {
return func && func(...rest);
};
export const safeInvoke = (func, ...rest) => {
return func && func(...rest);
};
export const transformToForm = (obj, emptyInitialValues) => {
return _.pickBy(
obj,
@@ -432,11 +436,10 @@ export function flatObject(obj) {
const path = []; // current path
function dig(obj) {
if (obj !== Object(obj))
/*is primitive, end of path*/
return (flatObject[path.join('.')] = obj); /*<- value*/
if (obj !== Object(obj)) {
return (flatObject[path.join('.')] = obj);
}
//no? so this is an object with keys. go deeper on each key down
for (let key in obj) {
path.push(key);
dig(obj[key]);