feat(Journal): add branch to topbar & entries columns.

This commit is contained in:
elforjani13
2022-02-28 14:29:56 +02:00
parent cb9c7fcdb6
commit eb340269c0
12 changed files with 288 additions and 9 deletions

View File

@@ -0,0 +1,115 @@
import React from 'react';
import intl from 'react-intl-universal';
import { MenuItem } from '@blueprintjs/core';
import { Suggest } from '@blueprintjs/select';
import { FormattedMessage as T } from 'components';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
/**
* branch suggest field.
* @returns
*/
export default function BranchSuggestField({
branches,
initialBranchId,
selectedBranchId,
defaultSelectText = intl.get('select_branch'),
popoverFill = false,
onBranchSelected,
...suggestProps
}) {
const initialBranch = React.useMemo(
() => branches.some((b) => b.id === initialBranchId),
[initialBranchId, branches],
);
const [selectedBranch, setSelectedBranch] = React.useState(
initialBranch || null,
);
/**
*
* @param {*} branch
* @returns
*/
const branchItemRenderer = (branch, { handleClick, modifiers, query }) => {
return (
<MenuItem
// active={modifiers.active}
disabled={modifiers.disabled}
label={branch.code.toString()}
key={branch.id}
onClick={handleClick}
text={branch.name.toString()}
/>
);
};
/**
*
* @param {*} query
* @param {*} branch
* @param {*} _index
* @param {*} exactMatch
* @returns
*/
const branchItemPredicate = (query, branch, _index, exactMatch) => {
const normalizedTitle = branch.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}
};
/**
*
* @param {*} branch
* @returns
*/
const brnachItemSelect = React.useCallback(
(branch) => {
if (branch.id) {
setSelectedBranch({ ...branch });
onBranchSelected && onBranchSelected(branch);
}
},
[setSelectedBranch, onBranchSelected],
);
/**
*
* @param {*} inputVaue
* @returns
*/
const branchInputValueRenderer = (inputValue) => {
if (inputValue) {
return inputValue.name.toString();
}
return '';
};
return (
<Suggest
items={branches}
noResults={<MenuItem disabled={true} text={<T id={'no_accounts'} />} />}
itemRenderer={branchItemRenderer}
itemPredicate={branchItemPredicate}
onItemSelect={brnachItemSelect}
selectedItem={selectedBranch}
inputProps={{ placeholder: defaultSelectText }}
resetOnClose={true}
fill={true}
popoverProps={{ minimal: true, boundary: 'window' }}
inputValueRenderer={branchInputValueRenderer}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
{...suggestProps}
/>
);
}

View File

@@ -0,0 +1,40 @@
import React from 'react';
import { FormGroup, Intent, Classes } from '@blueprintjs/core';
import classNames from 'classnames';
import BranchSuggestField from '../BranchSuggestField';
/**
* Branches list field cell.
* @returns
*/
export default function BranchesListFieldCell({
column: { id },
row: { index, original },
payload: { branches, updateData, errors },
}) {
const handleBranchSelected = React.useCallback(
(branch) => {
updateData(index, 'brnach_id', branch.id);
},
[updateData, index],
);
const error = errors?.[index]?.[id];
return (
<FormGroup
intent={error ? Intent.DANGER : null}
className={classNames(
'form-group--select-list',
'form-group--contacts-list',
Classes.FILL,
)}
>
<BranchSuggestField
branches={branches}
onBranchSelected={handleBranchSelected}
selectedBranchId={original?.contact_id}
/>
</FormGroup>
);
}

View File

@@ -9,6 +9,7 @@ import NumericInputCell from './NumericInputCell';
import CheckBoxFieldCell from './CheckBoxFieldCell';
import SwitchFieldCell from './SwitchFieldCell';
import TextAreaCell from './TextAreaCell';
import BranchesListFieldCell from './BranchesListFieldCell';
export {
AccountsListFieldCell,
@@ -23,4 +24,5 @@ export {
CheckBoxFieldCell,
SwitchFieldCell,
TextAreaCell,
BranchesListFieldCell,
};

View File

@@ -16,6 +16,7 @@ const Schema = Yup.object().shape({
date: Yup.date().required().label(intl.get('date')),
currency_code: Yup.string().max(3),
publish: Yup.boolean(),
branch_id: Yup.string(),
reference: Yup.string().nullable().min(1).max(DATATYPES_LENGTH.STRING),
description: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
exchange_rate: Yup.number(),

View File

@@ -10,7 +10,7 @@ import { useMakeJournalFormContext } from './MakeJournalProvider';
* Make journal entries field.
*/
export default function MakeJournalEntriesField() {
const { accounts, contacts } = useMakeJournalFormContext();
const { accounts, contacts ,branches } = useMakeJournalFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
@@ -18,6 +18,7 @@ export default function MakeJournalEntriesField() {
name={'entries'}
contacts={contacts}
accounts={accounts}
branches={branches}
shouldUpdate={entriesFieldShouldUpdate}
>
{({

View File

@@ -17,6 +17,7 @@ import MakeJournalFormFloatingActions from './MakeJournalFormFloatingActions';
import MakeJournalEntriesField from './MakeJournalEntriesField';
import MakeJournalFormFooter from './MakeJournalFormFooter';
import MakeJournalFormDialogs from './MakeJournalFormDialogs';
import MakeJournalFormTopBar from './MakeJournalFormTopBar';
import withSettings from 'containers/Settings/withSettings';
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
@@ -169,6 +170,7 @@ function MakeJournalEntriesForm({
onSubmit={handleSubmit}
>
<Form>
<MakeJournalFormTopBar />
<MakeJournalEntriesHeader />
<MakeJournalEntriesField />
<MakeJournalFormFooter />

View File

@@ -25,11 +25,12 @@ export default function MakeJournalEntriesTable({
minLinesNumber = 4,
currencyCode,
}) {
const { accounts, contacts } = useMakeJournalFormContext();
const { accounts, contacts, branches } = useMakeJournalFormContext();
// Memorized data table columns.
const columns = useJournalTableEntriesColumns();
// Handles update datatable data.
const handleUpdateData = (rowIndex, columnId, value) => {
const newRows = compose(
@@ -69,6 +70,7 @@ export default function MakeJournalEntriesTable({
updateData: handleUpdateData,
removeRow: handleRemoveRow,
contacts,
branches,
autoFocus: ['account_id', 0],
currencyCode,
}}

View File

@@ -0,0 +1,68 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Button, Alignment, NavbarGroup, Classes } from '@blueprintjs/core';
import styled from 'styled-components';
import { useSetPrimaryBranchToForm } from './utils';
import { useFeatureCan } from 'hooks/state';
import {
Icon,
BranchSelect,
FeatureCan,
FormTopbar,
DetailsBarSkeletonBase,
} from 'components';
import { useMakeJournalFormContext } from './MakeJournalProvider';
import { Features } from 'common';
/**
* Make journal form topbar.
* @returns
*/
export default function MakeJournalFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if branches feature is not enabled.
if (!featureCan(Features.Branches)) {
return null;
}
return (
<FormTopbar>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<MakeJournalFormSelectBranch />
</FeatureCan>
</NavbarGroup>
</FormTopbar>
);
}
function MakeJournalFormSelectBranch() {
// Invoice form context.
const { branches, isBranchesLoading } = useMakeJournalFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={MakeJournalBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function MakeJournalBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}

View File

@@ -9,6 +9,7 @@ import {
useCreateJournal,
useEditJournal,
useSettings,
useBranches,
useSettingsManualJournals,
} from 'hooks/query';
@@ -42,10 +43,20 @@ function MakeJournalProvider({ journalId, baseCurrency, ...props }) {
// Loading the journal settings.
const { isLoading: isSettingsLoading } = useSettingsManualJournals();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Submit form payload.
const [submitPayload, setSubmitPayload] = useState({});
const [selectJournalCurrency, setSelactJournalCurrency] = useState(null);
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isBranchesLoading;
const isForeignJournal =
!isEqual(selectJournalCurrency?.currency_code, baseCurrency) &&
!isUndefined(selectJournalCurrency?.currency_code);
@@ -56,6 +67,7 @@ function MakeJournalProvider({ journalId, baseCurrency, ...props }) {
currencies,
manualJournal,
baseCurrency,
branches,
createJournalMutate,
editJournalMutate,
@@ -64,8 +76,10 @@ function MakeJournalProvider({ journalId, baseCurrency, ...props }) {
isContactsLoading,
isCurrenciesLoading,
isJournalLoading,
isFeatureLoading,
isSettingsLoading,
isForeignJournal,
isBranchesSuccess,
isNewMode: !journalId,
submitPayload,

View File

@@ -8,8 +8,11 @@ import {
MoneyFieldCell,
InputGroupCell,
ContactsListFieldCell,
BranchesListFieldCell,
} from 'components/DataTableCells';
import { safeSumBy } from 'utils';
import { useFeatureCan } from 'hooks/state';
import { Features } from 'common';
/**
* Contact header cell.
@@ -44,11 +47,7 @@ export function DebitHeaderCell({ payload: { currencyCode } }) {
* Account footer cell.
*/
function AccountFooterCell({ payload: { currencyCode } }) {
return (
<span>
{intl.get('total_currency', { currency: currencyCode })}
</span>
);
return <span>{intl.get('total_currency', { currency: currencyCode })}</span>;
}
/**
@@ -107,6 +106,8 @@ export const ActionsCellRenderer = ({
* Retrieve columns of make journal entries table.
*/
export const useJournalTableEntriesColumns = () => {
const { featureCan } = useFeatureCan();
return React.useMemo(
() => [
{
@@ -128,7 +129,7 @@ export const useJournalTableEntriesColumns = () => {
className: 'account',
disableSortBy: true,
width: 160,
fieldProps: { allowCreate: true }
fieldProps: { allowCreate: true },
},
{
Header: CreditHeaderCell,
@@ -157,6 +158,19 @@ export const useJournalTableEntriesColumns = () => {
disableSortBy: true,
width: 120,
},
...(featureCan(Features.Branches)
? [
{
Header: intl.get('branch'),
id: 'branch_id',
accessor: 'branch_id',
Cell: BranchesListFieldCell,
className: 'branch',
disableSortBy: true,
width: 120,
},
]
: []),
{
Header: intl.get('note'),
accessor: 'note',

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Intent } from '@blueprintjs/core';
import { sumBy, setWith, toSafeInteger, get } from 'lodash';
import { sumBy, setWith, toSafeInteger, get, first } from 'lodash';
import moment from 'moment';
import * as R from 'ramda';
import {
@@ -14,6 +14,7 @@ import {
import { AppToaster } from 'components';
import intl from 'react-intl-universal';
import { useFormikContext } from 'formik';
import { useMakeJournalFormContext } from './MakeJournalProvider';
const ERROR = {
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
@@ -44,6 +45,7 @@ export const defaultManualJournal = {
reference: '',
currency_code: '',
publish: '',
branch_id: '',
exchange_rate: '',
entries: [...repeatValue(defaultEntry, 4)],
};
@@ -180,6 +182,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.accounts !== oldProps.accounts ||
newProps.contacts !== oldProps.contacts ||
newProps.branches !== oldProps.branches ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};
@@ -193,3 +196,18 @@ export const currenciesFieldShouldUpdate = (newProps, oldProps) => {
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useMakeJournalFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};

View File

@@ -1833,6 +1833,8 @@
"branches.column.address": "Address",
"branches.column.phone_number": "Phone number",
"branches.column.code": "Code",
"select_branch": "Select branch",
"branch": "Branch",
"branch.dialog.label_new_branch": "New Branch",
"branch.dialog.label_edit_branch": "New Branch",
"branch.dialog.label.branch_name": "Branch Name",