Merge branch 'develop' into e2e-onboarding

This commit is contained in:
Ahmed Bouhuolia
2023-06-23 16:12:03 +02:00
27 changed files with 539 additions and 623 deletions

View File

@@ -1,119 +0,0 @@
// @ts-nocheck
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { FormattedMessage as T } from '@/components';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
itemPredicate,
handleContactRenderer,
createNewItemRenderer,
createNewItemFromQuery,
} from './utils';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
function CustomerSelectFieldRoot({
// #withDrawerActions
openDrawer,
// #ownProps
contacts,
initialContactId,
selectedContactId,
defaultSelectText = <T id={'select_contact'} />,
onContactSelected,
popoverFill = false,
disabled = false,
allowCreate,
buttonProps,
...restProps
}) {
const localContacts = useMemo(
() =>
contacts.map((contact) => ({
...contact,
_id: `${contact.id}_${contact.contact_type}`,
})),
[contacts],
);
const initialContact = useMemo(
() => contacts.find((a) => a.id === initialContactId),
[initialContactId, contacts],
);
const [selecetedContact, setSelectedContact] = useState(
initialContact || null,
);
useEffect(() => {
if (typeof selectedContactId !== 'undefined') {
const account = selectedContactId
? contacts.find((a) => a.id === selectedContactId)
: null;
setSelectedContact(account);
}
}, [selectedContactId, contacts, setSelectedContact]);
const handleContactSelect = useCallback(
(contact) => {
if (contact.id) {
setSelectedContact({ ...contact });
onContactSelected && onContactSelected(contact);
} else {
openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER);
}
},
[setSelectedContact, onContactSelected, openDrawer],
);
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
return (
<Select
items={localContacts}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
itemRenderer={handleContactRenderer}
itemPredicate={itemPredicate}
filterable={true}
disabled={disabled}
onItemSelect={handleContactSelect}
popoverProps={{ minimal: true, usePortal: !popoverFill }}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
inputProps={{
placeholder: intl.get('filter_'),
}}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
createNewItemPosition={'top'}
{...restProps}
>
<Button
disabled={disabled}
text={
selecetedContact ? selecetedContact.display_name : defaultSelectText
}
{...buttonProps}
/>
</Select>
);
}
export const CustomerSelectField = R.compose(withDrawerActions)(
CustomerSelectFieldRoot,
);

View File

@@ -0,0 +1,48 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { createNewItemFromQuery, createNewItemRenderer } from './utils';
import { FSelect } from '../Forms';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
/**
* Customer select field.
* @returns {React.ReactNode}
*/
function CustomerSelectRoot({
// #withDrawerActions
openDrawer,
// #ownProps
items,
allowCreate,
...props
}) {
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
// Handles the create item click.
const handleCreateItemClick = () => {
openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER);
};
return (
<FSelect
items={items}
textAccessor={'display_name'}
labelAccessor={'code'}
valueAccessor={'id'}
popoverProps={{ minimal: true, usePortal: true, inline: false }}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
onCreateItemSelect={handleCreateItemClick}
{...props}
/>
);
}
export const CustomersSelect = R.compose(withDrawerActions)(CustomerSelectRoot);

View File

@@ -1,3 +1,3 @@
// @ts-nocheck
export * from './CustomerSelectField';
export * from './CustomerDrawerLink';
export * from './CustomersSelect';

View File

@@ -1,118 +0,0 @@
// @ts-nocheck
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { FormattedMessage as T } from '@/components';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
itemPredicate,
handleContactRenderer,
createNewItemFromQuery,
createNewItemRenderer,
} from './utils';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
function VendorSelectFieldRoot({
// #withDrawerActions
openDrawer,
// #ownProps
contacts,
initialContactId,
selectedContactId,
defaultSelectText = <T id={'select_contact'} />,
onContactSelected,
popoverFill = false,
disabled = false,
allowCreate,
buttonProps,
...restProps
}) {
const localContacts = useMemo(
() =>
contacts.map((contact) => ({
...contact,
_id: `${contact.id}_${contact.contact_type}`,
})),
[contacts],
);
const initialContact = useMemo(
() => contacts.find((a) => a.id === initialContactId),
[initialContactId, contacts],
);
const [selecetedContact, setSelectedContact] = useState(
initialContact || null,
);
useEffect(() => {
if (typeof selectedContactId !== 'undefined') {
const account = selectedContactId
? contacts.find((a) => a.id === selectedContactId)
: null;
setSelectedContact(account);
}
}, [selectedContactId, contacts, setSelectedContact]);
const handleContactSelect = useCallback(
(contact) => {
if (contact.id) {
setSelectedContact({ ...contact });
onContactSelected && onContactSelected(contact);
} else {
openDrawer(DRAWERS.QUICK_WRITE_VENDOR);
}
},
[setSelectedContact, onContactSelected, openDrawer],
);
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
return (
<Select
items={localContacts}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
itemRenderer={handleContactRenderer}
itemPredicate={itemPredicate}
filterable={true}
disabled={disabled}
onItemSelect={handleContactSelect}
popoverProps={{ minimal: true, usePortal: !popoverFill }}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
inputProps={{
placeholder: intl.get('filter_'),
}}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
createNewItemPosition={'top'}
{...restProps}
>
<Button
disabled={disabled}
text={
selecetedContact ? selecetedContact.display_name : defaultSelectText
}
{...buttonProps}
/>
</Select>
);
}
export const VendorSelectField = R.compose(withDrawerActions)(
VendorSelectFieldRoot,
);

View File

@@ -0,0 +1,49 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { createNewItemFromQuery, createNewItemRenderer } from './utils';
import { FSelect } from '../Forms';
import { DRAWERS } from '@/constants/drawers';
/**
* Vendor select.
* @returns {React.ReactNode}
*/
function VendorsSelectRoot({
// #withDrawerActions
openDrawer,
// #ownProps
items,
allowCreate,
...restProps
}) {
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
// Handles the create item click.
const handleCreateItemClick = () => {
openDrawer(DRAWERS.QUICK_WRITE_VENDOR);
};
return (
<FSelect
items={items}
textAccessor={'display_name'}
labelAccessor={'code'}
valueAccessor={'id'}
popoverProps={{ minimal: true, usePortal: true, inline: false }}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
onCreateItemSelect={handleCreateItemClick}
{...restProps}
/>
);
}
export const VendorsSelect = R.compose(withDrawerActions)(VendorsSelectRoot);

View File

@@ -1,3 +1,3 @@
// @ts-nocheck
export * from './VendorDrawerLink'
export * from './VendorSelectField'
export * from './VendorsSelect';

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components';
import { CustomersSelect, FormattedMessage as T } from '@/components';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
@@ -121,33 +121,39 @@ export default function ExpenseFormHeader() {
)}
</FastField>
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer'} />}
className={classNames('form-group--select-list', Classes.FILL)}
labelInfo={<Hint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'assign_to_customer'} />}
inline={true}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
}}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Customer ----------- */}
<ExpenseFormCustomerSelect />
</div>
);
}
/**
* Customer select field of expense form.
* @returns {React.ReactNode}
*/
function ExpenseFormCustomerSelect() {
const { customers } = useExpenseFormContext();
return (
<FormGroup
label={<T id={'customer'} />}
labelInfo={<Hint />}
inline={true}
name={'customer_id'}
fastField={true}
shouldUpdateDeps={{ items: customers }}
shouldUpdate={customersFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
allowCreate={true}
popoverFill={true}
fastField={true}
shouldUpdateDeps={{ items: customers }}
shouldUpdate={customersFieldShouldUpdate}
/>
</FormGroup>
);
}

View File

@@ -104,7 +104,7 @@ export const transformToEditForm = (
*/
export const customersFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -15,11 +15,10 @@ import {
FMoneyInputGroup,
InputPrependText,
FormattedMessage as T,
FieldRequiredHint,
CustomerSelectField,
Stack,
CustomersSelect,
} from '@/components';
import { inputIntent, momentFormatter } from '@/utils';
import { momentFormatter } from '@/utils';
import { useProjectFormContext } from './ProjectFormProvider';
/**
@@ -27,9 +26,6 @@ import { useProjectFormContext } from './ProjectFormProvider';
* @returns
*/
function ProjectFormFields() {
// project form dialog context.
const { customers } = useProjectFormContext();
// Formik context.
const { values } = useFormikContext();
@@ -37,26 +33,7 @@ function ProjectFormFields() {
<div className={Classes.DIALOG_BODY}>
<Stack spacing={25}>
{/*------------ Contact -----------*/}
<FastField name={'contact_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={intl.get('projects.dialog.contact')}
className={classNames('form-group--select-list', Classes.FILL)}
intent={inputIntent({ error, touched })}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={'Find or create a customer'}
onContactSelected={(customer) => {
form.setFieldValue('contact_id', customer.id);
}}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
)}
</FastField>
<ProjectFormCustomerSelect />
{/*------------ Project Name -----------*/}
<FFormGroup
@@ -113,4 +90,21 @@ function ProjectFormFields() {
);
}
function ProjectFormCustomerSelect() {
// project form dialog context.
const { customers } = useProjectFormContext();
return (
<FormGroup name={'contact_id'} label={intl.get('projects.dialog.contact')}>
<CustomersSelect
name={'contact_id'}
items={customers}
placeholder={'Find or create a customer'}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
);
}
export default ProjectFormFields;

View File

@@ -2,18 +2,18 @@
import React from 'react';
import styled from 'styled-components';
import classNames from 'classnames';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FormGroup, InputGroup, Classes, Position } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import { FeatureCan, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
VendorSelectField,
FieldRequiredHint,
Icon,
VendorDrawerLink,
VendorsSelect,
} from '@/components';
import { useBillFormContext } from './BillFormProvider';
@@ -43,43 +43,7 @@ function BillFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------- Vendor name ------ */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames(
'form-group--vendor-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<BillFormVendorField />
{/* ----------- Exchange rate ----------- */}
<BillExchangeRateInputField
@@ -190,6 +154,46 @@ function BillFormHeader() {
);
}
/**
* Vendor select field of bill form.
* @returns {JSX.Element}
*/
function BillFormVendorField() {
const { values, setFieldValue } = useFormikContext();
const { vendors } = useBillFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
}}
allowCreate={true}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withDialogActions)(BillFormHeader);
const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -19,7 +19,10 @@ import {
ensureEntriesHaveEmptyLine,
} from '@/containers/Entries/utils';
import { useCurrentOrganization } from '@/hooks/state';
import { isLandedCostDisabled, getEntriesTotal } from '@/containers/Entries/utils';
import {
isLandedCostDisabled,
getEntriesTotal,
} from '@/containers/Entries/utils';
import { useBillFormContext } from './BillFormProvider';
export const MIN_LINES_NUMBER = 1;
@@ -153,7 +156,7 @@ export const handleDeleteErrors = (errors) => {
*/
export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.vendors !== oldProps.vendors ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -9,17 +9,17 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
VendorSelectField,
FieldRequiredHint,
InputPrependButton,
Icon,
FormattedMessage as T,
VendorDrawerLink,
VendorsSelect,
} from '@/components';
import {
vendorsFieldShouldUpdate,
@@ -51,9 +51,6 @@ function VendorCreditNoteFormHeaderFields({
vendorcreditNumberPrefix,
vendorcreditNextNumber,
}) {
// Vendor Credit form context.
const { vendors } = useVendorCreditNoteFormContext();
// Handle vendor credit number changing.
const handleVendorCreditNumberChange = () => {
openDialog('vendor-credit-form');
@@ -81,39 +78,7 @@ function VendorCreditNoteFormHeaderFields({
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Vendor name ----------- */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--vendor')}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<VendorCreditFormVendorSelect />
{/* ----------- Exchange rate ----------- */}
<VendorCreditNoteExchangeRateInputField
@@ -205,6 +170,49 @@ function VendorCreditNoteFormHeaderFields({
);
}
/**
* Vendor select field of vendor credit form.
* @returns {React.ReactNode}
*/
function VendorCreditFormVendorSelect() {
const { values, setFieldValue } = useFormikContext();
// Vendor Credit form context.
const { vendors } = useVendorCreditNoteFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(
withDialogActions,
withSettings(({ vendorsCreditNoteSetting }) => ({

View File

@@ -113,7 +113,7 @@ export const transformFormValuesToRequest = (values) => {
*/
export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.vendors !== oldProps.vendors ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -12,14 +12,13 @@ import {
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components';
import { FormattedMessage as T, VendorsSelect } from '@/components';
import { toSafeInteger } from 'lodash';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
AccountsSelect,
VendorSelectField,
FieldRequiredHint,
InputPrependText,
Money,
@@ -55,8 +54,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
} = useFormikContext();
// Payment made form context.
const { vendors, accounts, isNewMode, setPaymentVendorId } =
usePaymentMadeFormContext();
const { accounts } = usePaymentMadeFormContext();
// Sumation of payable full-amount.
const payableFullAmount = useMemo(
@@ -82,41 +80,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------ Vendor name ------------ */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames('form-group--select-list', Classes.FILL)}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
setPaymentVendorId(contact.id);
}}
disabled={!isNewMode}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<PaymentFormVendorSelect />
{/* ----------- Exchange rate ----------- */}
<PaymentMadeExchangeRateInputField
@@ -258,6 +222,52 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
);
}
/**
* Vendor select field of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentFormVendorSelect() {
// Formik form context.
const { values, setFieldValue } = useFormikContext();
// Payment made form context.
const { vendors, isNewMode, setPaymentVendorId } =
usePaymentMadeFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
setPaymentVendorId(contact.id);
}}
disabled={!isNewMode}
allowCreate={true}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withCurrentOrganization())(PaymentMadeFormHeaderFields);
const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -74,7 +74,7 @@ export const transformToNewPageEntries = (entries) => {
*/
export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.vendors !== oldProps.vendors ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -4,15 +4,16 @@ import classNames from 'classnames';
import styled from 'styled-components';
import { FormGroup, InputGroup, Position } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes';
import {
CustomerSelectField,
FieldRequiredHint,
Icon,
FormattedMessage as T,
CustomerDrawerLink,
FFormGroup,
CustomersSelect,
} from '@/components';
import { customerNameFieldShouldUpdate } from './utils';
@@ -30,49 +31,10 @@ import {
* Credit note form header fields.
*/
export default function CreditNoteFormHeaderFields({}) {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customerNameFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(
'form-group--customer-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<CreditNoteCustomersSelect />
{/* ----------- Exchange rate ----------- */}
<CreditNoteExchangeRateInputField
@@ -125,6 +87,48 @@ export default function CreditNoteFormHeaderFields({}) {
);
}
/**
* Customer select field of credit note form.
* @returns {React.ReactNode}
*/
function CreditNoteCustomersSelect() {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
const { setFieldValue, values } = useFormikContext();
return (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -116,7 +116,7 @@ export const transformFormValuesToRequest = (values) => {
*/
export const customerNameFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -4,16 +4,16 @@ import styled from 'styled-components';
import classNames from 'classnames';
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import {
FeatureCan,
FFormGroup,
FormattedMessage as T,
CustomerSelectField,
FieldRequiredHint,
Icon,
CustomerDrawerLink,
CustomersSelect,
} from '@/components';
import {
momentFormatter,
@@ -42,41 +42,7 @@ export default function EstimateFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--customer')}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
intent={inputIntent({ error, touched })}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<EstimateFormCustomerSelect />
{/* ----------- Exchange Rate ----------- */}
<EstimateExchangeRateInputField
@@ -177,6 +143,47 @@ export default function EstimateFormHeader() {
);
}
/**
* Customer select field of estimate form.
* @returns {React.ReactNode}
*/
function EstimateFormCustomerSelect() {
const { setFieldValue, values } = useFormikContext();
const { customers } = useEstimateFormContext();
return (
<FFormGroup
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
name={'customer_id'}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -80,7 +80,7 @@ export const transformToEditForm = (estimate) => {
*/
export const customersFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -12,9 +12,9 @@ import {
Col,
Row,
CustomerDrawerLink,
CustomerSelectField,
FieldRequiredHint,
FeatureCan,
CustomersSelect,
} from '@/components';
import {
momentFormatter,
@@ -42,49 +42,13 @@ import { Features } from '@/constants';
*/
export default function InvoiceFormHeaderFields() {
// Invoice form context.
const { customers, projects } = useInvoiceFormContext();
const { projects } = useInvoiceFormContext();
const { values } = useFormikContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customerNameFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
inline={true}
className={classNames(
'form-group--customer-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
)}
</FastField>
<InvoiceFormCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<InvoiceExchangeRateInputField
@@ -192,6 +156,46 @@ export default function InvoiceFormHeaderFields() {
);
}
/**
* Customer select field of the invoice form.
* @returns {React.ReactNode}
*/
function InvoiceFormCustomerSelect() {
const { customers } = useInvoiceFormContext();
const { values, setFieldValue } = useFormikContext();
return (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
allowCreate={true}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -114,7 +114,7 @@ export const transformErrors = (errors, { setErrors }) => {
*/
export const customerNameFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -14,8 +14,11 @@ import { DateInput } from '@blueprintjs/datetime';
import { toSafeInteger } from 'lodash';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import { FeatureCan, FormattedMessage as T } from '@/components';
import { useAutofocus } from '@/hooks';
import {
FeatureCan,
CustomersSelect,
FormattedMessage as T,
} from '@/components';
import { CLASSES } from '@/constants/classes';
import {
safeSumBy,
@@ -27,7 +30,6 @@ import {
import {
FFormGroup,
AccountsSelect,
CustomerSelectField,
FieldRequiredHint,
Icon,
MoneyInputGroup,
@@ -58,8 +60,7 @@ import { PaymentReceivePaymentNoField } from './PaymentReceivePaymentNoField';
*/
export default function PaymentReceiveHeaderFields() {
// Payment receive form context.
const { customers, accounts, projects, isNewMode } =
usePaymentReceiveFormContext();
const { accounts, projects } = usePaymentReceiveFormContext();
// Formik form context.
const {
@@ -67,8 +68,6 @@ export default function PaymentReceiveHeaderFields() {
setFieldValue,
} = useFormikContext();
const customerFieldRef = useAutofocus();
// Calculates the full-amount received.
const totalDueAmount = useMemo(
() => safeSumBy(entries, 'due_amount'),
@@ -91,45 +90,7 @@ export default function PaymentReceiveHeaderFields() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames('form-group--select-list', CLASSES.FILL)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('full_amount', '');
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
disabled={!isNewMode}
buttonProps={{
elementRef: (ref) => (customerFieldRef.current = ref),
}}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<PaymentReceiveCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<PaymentReceiveExchangeRateInputField
@@ -276,3 +237,49 @@ const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;
`;
/**
* Customer select field of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentReceiveCustomerSelect() {
// Payment receive form context.
const { customers, isNewMode } = usePaymentReceiveFormContext();
// Formik form context.
const { values, setFieldValue } = useFormikContext();
return (
<FFormGroup
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
name={'customer_id'}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('full_amount', '');
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
disabled={!isNewMode}
allowCreate={true}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}

View File

@@ -129,7 +129,7 @@ export const fullAmountPaymentEntries = (entries) => {
*/
export const customersFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -4,7 +4,7 @@ import styled from 'styled-components';
import classNames from 'classnames';
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes';
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
@@ -12,7 +12,7 @@ import { Features } from '@/constants';
import {
FFormGroup,
AccountsSelect,
CustomerSelectField,
CustomersSelect,
FieldRequiredHint,
Icon,
CustomerDrawerLink,
@@ -38,44 +38,12 @@ import { ReceiptFormReceiptNumberField } from './ReceiptFormReceiptNumberField';
* Receipt form header fields.
*/
export default function ReceiptFormHeader() {
const { accounts, customers, projects } = useReceiptFormContext();
const { accounts, projects } = useReceiptFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--customer')}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<ReceiptFormCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<ReceiptExchangeRateInputField
@@ -172,6 +140,47 @@ export default function ReceiptFormHeader() {
);
}
/**
* Customer select field of receipt form.
* @returns {React.ReactNode}
*/
function ReceiptFormCustomerSelect() {
const { setFieldValue, values } = useFormikContext();
const { customers } = useReceiptFormContext();
return (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -104,7 +104,7 @@ export const accountsFieldShouldUpdate = (newProps, oldProps) => {
*/
export const customersFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.customers !== oldProps.customers ||
newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};

View File

@@ -379,7 +379,7 @@
"the_expenses_have_been_deleted_successfully": "The expenses have been deleted successfully",
"once_delete_these_expenses_you_will_not_able_restore_them": "Once you delete these expenses, you won't be able to retrieve them later. Are you sure you want to delete them?",
"the_expense_has_been_published": "The expense has been published",
"select_customer": "Select customer",
"select_customer": "Select Customer...",
"total_amount_equals_zero": "Total amount equals zero",
"value": "Value",
"you_reached_conditions_limit": "You have reached to conditions limit.",
@@ -480,7 +480,7 @@
"estimate_date": "Estimate Date",
"expiration_date": "Expiration Date",
"customer_note": "Customer Note",
"select_customer_account": "Select Customer Account",
"select_customer_account": "Select Customer Account...",
"select_product": "Select Product",
"reference": "Reference #",
"clear": "Clear",