mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
Merge branch 'develop' into main
This commit is contained in:
@@ -13,13 +13,13 @@ const Schema = Yup.object().shape({
|
||||
.min(1)
|
||||
.max(DATATYPES_LENGTH.STRING)
|
||||
.label(intl.get('journal_type')),
|
||||
date: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('date')),
|
||||
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(),
|
||||
entries: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
credit: Yup.number().nullable(),
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
{({
|
||||
|
||||
@@ -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';
|
||||
@@ -58,6 +59,7 @@ function MakeJournalEntriesForm({
|
||||
journalNumberPrefix,
|
||||
journalNextNumber,
|
||||
);
|
||||
|
||||
// Form initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
@@ -68,12 +70,12 @@ function MakeJournalEntriesForm({
|
||||
: {
|
||||
...defaultManualJournal,
|
||||
...(journalAutoIncrement && {
|
||||
journal_number: defaultTo(journalNumber, ''),
|
||||
journal_number: journalNumber,
|
||||
}),
|
||||
currency_code: base_currency,
|
||||
}),
|
||||
}),
|
||||
[manualJournal, base_currency, journalNumber],
|
||||
[manualJournal, base_currency, journalNumber, journalAutoIncrement],
|
||||
);
|
||||
|
||||
// Handle the form submiting.
|
||||
@@ -107,7 +109,7 @@ function MakeJournalEntriesForm({
|
||||
return;
|
||||
}
|
||||
const form = {
|
||||
...omit(values, ['journal_number', 'journal_number_manually']),
|
||||
...omit(values, ['journal_number_manually']),
|
||||
...(values.journal_number_manually && {
|
||||
journal_number: values.journal_number,
|
||||
}),
|
||||
@@ -169,6 +171,7 @@ function MakeJournalEntriesForm({
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<MakeJournalFormTopBar />
|
||||
<MakeJournalEntriesHeader />
|
||||
<MakeJournalEntriesField />
|
||||
<MakeJournalFormFooter />
|
||||
@@ -185,7 +188,7 @@ function MakeJournalEntriesForm({
|
||||
export default compose(
|
||||
withMediaActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
journalNextNumber: parseInt(manualJournalsSettings?.nextNumber, 10),
|
||||
journalNextNumber: manualJournalsSettings?.nextNumber,
|
||||
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
|
||||
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
|
||||
})),
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from 'components';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { JournalExchangeRateInputField } from './components';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import {
|
||||
currenciesFieldShouldUpdate,
|
||||
@@ -52,14 +53,14 @@ function MakeJournalEntriesHeader({
|
||||
|
||||
// Handle journal number change.
|
||||
const handleJournalNumberChange = () => {
|
||||
openDialog('journal-number-form', {});
|
||||
openDialog('journal-number-form');
|
||||
};
|
||||
|
||||
// Handle journal number blur.
|
||||
const handleJournalNoBlur = (form, field) => (event) => {
|
||||
const newValue = event.target.value;
|
||||
|
||||
if (field.value !== newValue) {
|
||||
if (field.value !== newValue && journalAutoIncrement) {
|
||||
openDialog('journal-number-form', {
|
||||
initialFormValues: {
|
||||
manualTransactionNo: newValue,
|
||||
@@ -201,13 +202,19 @@ function MakeJournalEntriesHeader({
|
||||
selectedCurrencyCode={value}
|
||||
onCurrencySelected={(currencyItem) => {
|
||||
form.setFieldValue('currency_code', currencyItem.currency_code);
|
||||
form.setFieldValue('exchange_rate', '');
|
||||
}}
|
||||
defaultSelectText={value}
|
||||
disabled={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ----------- Exchange rate ----------- */}
|
||||
<JournalExchangeRateInputField
|
||||
name={'exchange_rate'}
|
||||
formGroupProps={{ label: ' ', inline: true }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { MakeJournalProvider } from './MakeJournalProvider';
|
||||
*/
|
||||
export default function MakeJournalEntriesPage() {
|
||||
const { id: journalId } = useParams();
|
||||
|
||||
|
||||
return (
|
||||
<MakeJournalProvider journalId={journalId}>
|
||||
<MakeJournalEntriesForm />
|
||||
|
||||
@@ -21,15 +21,16 @@ export default function MakeJournalEntriesTable({
|
||||
entries,
|
||||
defaultEntry,
|
||||
error,
|
||||
initialLinesNumber = 4,
|
||||
minLinesNumber = 4,
|
||||
initialLinesNumber = 1,
|
||||
minLinesNumber = 1,
|
||||
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(
|
||||
@@ -62,13 +63,13 @@ export default function MakeJournalEntriesTable({
|
||||
data={entries}
|
||||
sticky={true}
|
||||
totalRow={true}
|
||||
footer={true}
|
||||
payload={{
|
||||
accounts,
|
||||
errors: error,
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
contacts,
|
||||
branches,
|
||||
autoFocus: ['account_id', 0],
|
||||
currencyCode,
|
||||
}}
|
||||
|
||||
@@ -1,44 +1,29 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Postbox, ErrorMessage, Row, Col } from 'components';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
import { inputIntent } from 'utils';
|
||||
import { Row, Col, Paper } from 'components';
|
||||
import { MakeJournalFormFooterLeft } from './MakeJournalFormFooterLeft';
|
||||
import { MakeJournalFormFooterRight } from './MakeJournalFormFooterRight';
|
||||
|
||||
export default function MakeJournalFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Postbox title={<T id={'journal_details'} />} defaultOpen={false}>
|
||||
<MakeJournalFooterPaper>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="description" />}
|
||||
fill={true}
|
||||
>
|
||||
<TextArea fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<MakeJournalFormFooterLeft />
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={<T id={'attachments_maximum'} />}
|
||||
/>
|
||||
<MakeJournalFormFooterRight />
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</MakeJournalFooterPaper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const MakeJournalFooterPaper = styled(Paper)`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { FFormGroup, FEditableText, FormattedMessage as T } from 'components';
|
||||
|
||||
export function MakeJournalFormFooterLeft() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* --------- Description --------- */}
|
||||
<DescriptionFormGroup
|
||||
label={<T id={'description'} />}
|
||||
name={'description'}
|
||||
>
|
||||
<FEditableText
|
||||
name={'description'}
|
||||
placeholder={<T id={'make_jorunal.decscrption.placeholder'} />}
|
||||
/>
|
||||
</DescriptionFormGroup>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const DescriptionFormGroup = styled(FFormGroup)`
|
||||
&.bp3-form-group {
|
||||
.bp3-label {
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.bp3-form-content {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from 'components';
|
||||
import { useJournalTotals } from './utils';
|
||||
|
||||
export function MakeJournalFormFooterRight() {
|
||||
const { formattedSubtotal, formattedTotal } = useJournalTotals();
|
||||
|
||||
return (
|
||||
<MakeJouranlTotalLines>
|
||||
<TotalLine
|
||||
title={<T id={'make_journal.label.subtotal'} />}
|
||||
value={formattedSubtotal}
|
||||
borderStyle={TotalLineBorderStyle.None}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'make_journal.label.total'} />}
|
||||
value={formattedTotal}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</MakeJouranlTotalLines>
|
||||
);
|
||||
}
|
||||
|
||||
const MakeJouranlTotalLines = styled(TotalLines)`
|
||||
width: 100%;
|
||||
color: #555555;
|
||||
`;
|
||||
@@ -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('make_journal.branch_button.label', { label })}
|
||||
minimal={true}
|
||||
small={true}
|
||||
icon={<Icon icon={'branch-16'} iconSize={16} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import React, { createContext, useState } from 'react';
|
||||
import { isEqual, isUndefined } from 'lodash';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useAccounts,
|
||||
@@ -8,7 +11,8 @@ import {
|
||||
useCreateJournal,
|
||||
useEditJournal,
|
||||
useSettings,
|
||||
useSettingsManualJournals
|
||||
useBranches,
|
||||
useSettingsManualJournals,
|
||||
} from 'hooks/query';
|
||||
|
||||
const MakeJournalFormContext = createContext();
|
||||
@@ -16,15 +20,17 @@ const MakeJournalFormContext = createContext();
|
||||
/**
|
||||
* Make journal form provider.
|
||||
*/
|
||||
function MakeJournalProvider({ journalId, ...props }) {
|
||||
function MakeJournalProvider({ journalId, query, ...props }) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Load the accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
// Load the customers list.
|
||||
const {
|
||||
data: contacts,
|
||||
isLoading: isContactsLoading,
|
||||
} = useAutoCompleteContacts();
|
||||
const { data: contacts, isLoading: isContactsLoading } =
|
||||
useAutoCompleteContacts();
|
||||
|
||||
// Load the currencies list.
|
||||
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
|
||||
@@ -43,15 +49,27 @@ function MakeJournalProvider({ journalId, ...props }) {
|
||||
// Loading the journal settings.
|
||||
const { isLoading: isSettingsLoading } = useSettingsManualJournals();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Submit form payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
// Determines whether the warehouse and branches are loading.
|
||||
const isFeatureLoading = isBranchesLoading;
|
||||
|
||||
const provider = {
|
||||
accounts,
|
||||
contacts,
|
||||
currencies,
|
||||
manualJournal,
|
||||
|
||||
branches,
|
||||
|
||||
createJournalMutate,
|
||||
editJournalMutate,
|
||||
|
||||
@@ -59,12 +77,13 @@ function MakeJournalProvider({ journalId, ...props }) {
|
||||
isContactsLoading,
|
||||
isCurrenciesLoading,
|
||||
isJournalLoading,
|
||||
isFeatureLoading,
|
||||
isSettingsLoading,
|
||||
|
||||
isBranchesSuccess,
|
||||
isNewMode: !journalId,
|
||||
|
||||
submitPayload,
|
||||
setSubmitPayload
|
||||
setSubmitPayload,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -73,7 +92,7 @@ function MakeJournalProvider({ journalId, ...props }) {
|
||||
isJournalLoading ||
|
||||
isAccountsLoading ||
|
||||
isCurrenciesLoading ||
|
||||
isContactsLoading ||
|
||||
isContactsLoading ||
|
||||
isSettingsLoading
|
||||
}
|
||||
name={'make-journal-page'}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import React from 'react';
|
||||
import { Intent, Position, Button, Tooltip } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Icon, Money, Hint } from 'components';
|
||||
import { Menu, MenuItem, Position, Button } from '@blueprintjs/core';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import { useFormikContext } from 'formik';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { ExchangeRateInputGroup, Icon, Hint, FormattedMessage as T } from 'components';
|
||||
import {
|
||||
AccountsListFieldCell,
|
||||
MoneyFieldCell,
|
||||
InputGroupCell,
|
||||
ContactsListFieldCell,
|
||||
BranchesListFieldCell,
|
||||
} from 'components/DataTableCells';
|
||||
import { safeSumBy } from 'utils';
|
||||
|
||||
import { CellType, Features, Align } from 'common';
|
||||
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import { useCurrentOrganization } from 'hooks/state';
|
||||
import { useJournalIsForeign } from './utils';
|
||||
|
||||
/**
|
||||
* Contact header cell.
|
||||
@@ -40,42 +48,6 @@ export function DebitHeaderCell({ payload: { currencyCode } }) {
|
||||
return intl.get('debit_currency', { currency: currencyCode });
|
||||
}
|
||||
|
||||
/**
|
||||
* Account footer cell.
|
||||
*/
|
||||
function AccountFooterCell({ payload: { currencyCode } }) {
|
||||
return (
|
||||
<span>
|
||||
{intl.get('total_currency', { currency: currencyCode })}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total credit table footer cell.
|
||||
*/
|
||||
function TotalCreditFooterCell({ payload: { currencyCode }, rows }) {
|
||||
const credit = safeSumBy(rows, 'original.credit');
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Money amount={credit} currency={currencyCode} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total debit table footer cell.
|
||||
*/
|
||||
function TotalDebitFooterCell({ payload: { currencyCode }, rows }) {
|
||||
const debit = safeSumBy(rows, 'original.debit');
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Money amount={debit} currency={currencyCode} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Actions cell renderer.
|
||||
*/
|
||||
@@ -86,95 +58,120 @@ export const ActionsCellRenderer = ({
|
||||
data,
|
||||
payload,
|
||||
}) => {
|
||||
const onClickRemoveRole = () => {
|
||||
const handleClickRemoveRole = () => {
|
||||
payload.removeRow(index);
|
||||
};
|
||||
const exampleMenu = (
|
||||
<Menu>
|
||||
<MenuItem onClick={handleClickRemoveRole} text="Remove line" />
|
||||
</Menu>
|
||||
);
|
||||
return (
|
||||
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
|
||||
<Popover2 content={exampleMenu} placement="left-start">
|
||||
<Button
|
||||
icon={<Icon icon="times-circle" iconSize={14} />}
|
||||
icon={<Icon icon={'more-13'} iconSize={13} />}
|
||||
iconSize={14}
|
||||
className="ml2"
|
||||
className="m12"
|
||||
minimal={true}
|
||||
intent={Intent.DANGER}
|
||||
onClick={onClickRemoveRole}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Popover2>
|
||||
);
|
||||
};
|
||||
ActionsCellRenderer.cellType = CellType.Button;
|
||||
|
||||
/**
|
||||
* Retrieve columns of make journal entries table.
|
||||
*/
|
||||
export const useJournalTableEntriesColumns = () => {
|
||||
const { featureCan } = useFeatureCan();
|
||||
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: '#',
|
||||
accessor: 'index',
|
||||
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
|
||||
className: 'index',
|
||||
width: 40,
|
||||
disableResizing: true,
|
||||
disableSortBy: true,
|
||||
sticky: 'left',
|
||||
},
|
||||
{
|
||||
Header: intl.get('account'),
|
||||
id: 'account_id',
|
||||
accessor: 'account_id',
|
||||
Cell: AccountsListFieldCell,
|
||||
Footer: AccountFooterCell,
|
||||
className: 'account',
|
||||
disableSortBy: true,
|
||||
width: 160,
|
||||
fieldProps: { allowCreate: true }
|
||||
fieldProps: { allowCreate: true },
|
||||
},
|
||||
{
|
||||
Header: CreditHeaderCell,
|
||||
accessor: 'credit',
|
||||
Cell: MoneyFieldCell,
|
||||
Footer: TotalCreditFooterCell,
|
||||
className: 'credit',
|
||||
disableSortBy: true,
|
||||
width: 100,
|
||||
align: Align.Right,
|
||||
},
|
||||
{
|
||||
Header: DebitHeaderCell,
|
||||
accessor: 'debit',
|
||||
Cell: MoneyFieldCell,
|
||||
Footer: TotalDebitFooterCell,
|
||||
className: 'debit',
|
||||
disableSortBy: true,
|
||||
width: 100,
|
||||
align: Align.Right,
|
||||
},
|
||||
{
|
||||
Header: ContactHeaderCell,
|
||||
id: 'contact_id',
|
||||
accessor: 'contact_id',
|
||||
Cell: ContactsListFieldCell,
|
||||
className: 'contact',
|
||||
disableSortBy: true,
|
||||
width: 120,
|
||||
},
|
||||
...(featureCan(Features.Branches)
|
||||
? [
|
||||
{
|
||||
Header: intl.get('branch'),
|
||||
id: 'branch_id',
|
||||
accessor: 'branch_id',
|
||||
Cell: BranchesListFieldCell,
|
||||
disableSortBy: true,
|
||||
width: 120,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
Header: intl.get('note'),
|
||||
accessor: 'note',
|
||||
Cell: InputGroupCell,
|
||||
disableSortBy: true,
|
||||
className: 'note',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
Header: '',
|
||||
accessor: 'action',
|
||||
Cell: ActionsCellRenderer,
|
||||
className: 'actions',
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 45,
|
||||
align: Align.Center,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Journal exchange rate input field.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function JournalExchangeRateInputField({ ...props }) {
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const isForeignJouranl = useJournalIsForeign();
|
||||
|
||||
// Can't continue if the customer is not foreign.
|
||||
if (!isForeignJouranl) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ExchangeRateInputGroup
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={currentOrganization.base_currency}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -9,11 +9,15 @@ import {
|
||||
repeatValue,
|
||||
transformToForm,
|
||||
defaultFastFieldShouldUpdate,
|
||||
ensureEntriesHasEmptyLine
|
||||
ensureEntriesHasEmptyLine,
|
||||
formattedAmount,
|
||||
safeSumBy,
|
||||
} from 'utils';
|
||||
import { AppToaster } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { useCurrentOrganization } from 'hooks/state';
|
||||
|
||||
const ERROR = {
|
||||
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
|
||||
@@ -26,25 +30,30 @@ const ERROR = {
|
||||
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||
};
|
||||
|
||||
export const MIN_LINES_NUMBER = 4;
|
||||
export const MIN_LINES_NUMBER = 1;
|
||||
export const DEFAULT_LINES_NUMBER = 1;
|
||||
|
||||
export const defaultEntry = {
|
||||
account_id: '',
|
||||
credit: '',
|
||||
debit: '',
|
||||
contact_id: '',
|
||||
branch_id: '',
|
||||
note: '',
|
||||
};
|
||||
|
||||
export const defaultManualJournal = {
|
||||
journal_number: '',
|
||||
journal_number_manually: false,
|
||||
journal_type: 'Journal',
|
||||
date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
description: '',
|
||||
reference: '',
|
||||
currency_code: '',
|
||||
publish: '',
|
||||
entries: [...repeatValue(defaultEntry, 4)],
|
||||
branch_id: '',
|
||||
exchange_rate: 1,
|
||||
entries: [...repeatValue(defaultEntry, DEFAULT_LINES_NUMBER)],
|
||||
};
|
||||
|
||||
// Transform to edit form.
|
||||
@@ -179,6 +188,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.contacts !== oldProps.contacts ||
|
||||
newProps.branches !== oldProps.branches ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
@@ -192,3 +202,63 @@ 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]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retreives the Journal totals.
|
||||
*/
|
||||
export const useJournalTotals = () => {
|
||||
const {
|
||||
values: { entries, currency_code: currencyCode },
|
||||
} = useFormikContext();
|
||||
|
||||
// Retrieves the invoice entries total.
|
||||
const totalCredit = safeSumBy(entries, 'credit');
|
||||
const totalDebit = safeSumBy(entries, 'debit');
|
||||
|
||||
const total = Math.max(totalCredit, totalDebit);
|
||||
// Retrieves the formatted total money.
|
||||
const formattedTotal = React.useMemo(
|
||||
() => formattedAmount(total, currencyCode),
|
||||
[total, currencyCode],
|
||||
);
|
||||
// Retrieves the formatted subtotal.
|
||||
const formattedSubtotal = React.useMemo(
|
||||
() => formattedAmount(total, currencyCode, { money: false }),
|
||||
[total, currencyCode],
|
||||
);
|
||||
|
||||
return {
|
||||
formattedTotal,
|
||||
formattedSubtotal,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the expenses has foreign .
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const useJournalIsForeign = () => {
|
||||
const { values } = useFormikContext();
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
|
||||
const isForeignJournal = React.useMemo(
|
||||
() => values.currency_code !== currentOrganization.base_currency,
|
||||
[values.currency_code, currentOrganization.base_currency],
|
||||
);
|
||||
return isForeignJournal;
|
||||
};
|
||||
|
||||
79
src/containers/Alerts/Branches/BranchDeleteAlert.js
Normal file
79
src/containers/Alerts/Branches/BranchDeleteAlert.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useDeleteBranch } from 'hooks/query';
|
||||
import { handleDeleteErrors } from '../../Preferences/Branches/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Branch delete alert.
|
||||
*/
|
||||
function BranchDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { branchId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: deleteBranch, isLoading } = useDeleteBranch();
|
||||
|
||||
// Handle cancel delete alert.
|
||||
const handleCancelDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete branch.
|
||||
const handleConfirmDeleteBranch = () => {
|
||||
deleteBranch(branchId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('branch.alert.delete_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDelete}
|
||||
onConfirm={handleConfirmDeleteBranch}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage id={'branch.once_delete_this_branch'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(BranchDeleteAlert);
|
||||
70
src/containers/Alerts/Branches/BranchMarkPrimaryAlert.js
Normal file
70
src/containers/Alerts/Branches/BranchMarkPrimaryAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useMarkBranchAsPrimary } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* branch mark primary alert.
|
||||
*/
|
||||
function BranchMarkPrimaryAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { branchId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: markPrimaryBranchMutate, isLoading } =
|
||||
useMarkBranchAsPrimary();
|
||||
|
||||
// Handle cancel mark primary alert.
|
||||
const handleCancelMarkPrimaryAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// andle cancel mark primary confirm.
|
||||
const handleConfirmMarkPrimaryBranch = () => {
|
||||
markPrimaryBranchMutate(branchId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('branch.alert.mark_primary_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
// cancelButtonText={<T id={'cancel'} />}
|
||||
// confirmButtonText={<T id={'make_primary'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelMarkPrimaryAlert}
|
||||
onConfirm={handleConfirmMarkPrimaryBranch}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'branch.alert.are_you_sure_you_want_to_make'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(BranchMarkPrimaryAlert);
|
||||
80
src/containers/Alerts/Warehouses/WarehouseDeleteAlert.js
Normal file
80
src/containers/Alerts/Warehouses/WarehouseDeleteAlert.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useDeleteWarehouse } from 'hooks/query';
|
||||
import { handleDeleteErrors } from '../../Preferences/Warehouses/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Warehouse delete alert
|
||||
* @returns
|
||||
*/
|
||||
function WarehouseDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { warehouseId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: deleteWarehouseMutate, isLoading } =
|
||||
useDeleteWarehouse();
|
||||
|
||||
// handle cancel delete warehouse alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handleConfirm delete invoice
|
||||
const handleConfirmWarehouseDelete = () => {
|
||||
deleteWarehouseMutate(warehouseId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('warehouse.alert.delete_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmWarehouseDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage id={'warehouse.once_delete_this_warehouse'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(WarehouseDeleteAlert);
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useTransferredWarehouseTransfer } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* warehouse transfer transferred alert.
|
||||
* @returns
|
||||
*/
|
||||
function TransferredWarehouseTransferAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { warehouseTransferId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: transferredWarehouseTransferMutate, isLoading } =
|
||||
useTransferredWarehouseTransfer();
|
||||
|
||||
// handle cancel alert.
|
||||
const handleCancelAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm alert.
|
||||
const handleConfirmTransferred = () => {
|
||||
transferredWarehouseTransferMutate(warehouseTransferId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('warehouse_transfer.alert.transferred_warehouse'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'deliver'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmTransferred}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'warehouse_transfer.alert.are_you_sure_you_want_to_deliver'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(TransferredWarehouseTransferAlert);
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useMarkWarehouseAsPrimary } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* warehouse mark primary alert.
|
||||
*/
|
||||
function WarehouseMarkPrimaryAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { warehouseId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// const { mutateAsync: markPrimaryWarehouseMutate, isLoading } =
|
||||
// useMarkWarehouseAsPrimary();
|
||||
|
||||
// Handle cancel mark primary alert.
|
||||
const handleCancelMarkPrimaryAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// andle cancel mark primary confirm.
|
||||
const handleConfirmMarkPrimaryWarehouse = () => {
|
||||
markPrimaryWarehouseMutate(warehouseId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('warehouse.alert.mark_primary_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'make_primary'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelMarkPrimaryAlert}
|
||||
onConfirm={handleConfirmMarkPrimaryWarehouse}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'warehouse.alert.are_you_sure_you_want_to_make'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(WarehouseMarkPrimaryAlert);
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useDeleteWarehouseTransfer } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Warehouse transfer delete alert
|
||||
* @returns
|
||||
*/
|
||||
function WarehouseTransferDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { warehouseTransferId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteWarehouseTransferMutate, isLoading } =
|
||||
useDeleteWarehouseTransfer();
|
||||
|
||||
// handle cancel delete warehouse alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handleConfirm delete warehouse transfer.
|
||||
const handleConfirmWarehouseTransferDelete = () => {
|
||||
deleteWarehouseTransferMutate(warehouseTransferId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('warehouse_transfer.alert.delete_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('warehouse-transfer-detail-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmWarehouseTransferDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'warehouse_transfer.once_delete_this_warehouse_transfer'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(WarehouseTransferDeleteAlert);
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useInitiateWarehouseTransfer } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* warehouse transfer initiate alert.
|
||||
* @returns
|
||||
*/
|
||||
function WarehouseTransferInitiateAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { warehouseTransferId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: initialWarehouseTransferMutate, isLoading } =
|
||||
useInitiateWarehouseTransfer();
|
||||
|
||||
// handle cancel alert.
|
||||
const handleCancelAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm alert.
|
||||
const handleConfirmInitiated = () => {
|
||||
initialWarehouseTransferMutate(warehouseTransferId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('warehouse_transfer.alert.initiate_warehouse'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'warehouse_transfer.label.initiate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmInitiated}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'warehouse_transfer.alert.are_you_sure_you_want_to_initate'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(WarehouseTransferInitiateAlert);
|
||||
@@ -19,7 +19,10 @@ import CurrenciesAlerts from '../Preferences/Currencies/CurrenciesAlerts';
|
||||
import RolesAlerts from '../Preferences/Users/Roles/RolesAlerts';
|
||||
import CreditNotesAlerts from '../Sales/CreditNotes/CreditNotesAlerts';
|
||||
import VendorCreditNotesAlerts from '../Purchases/CreditNotes/VendorCreditNotesAlerts';
|
||||
import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts'
|
||||
import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts';
|
||||
import WarehousesAlerts from '../Preferences/Warehouses/WarehousesAlerts';
|
||||
import WarehousesTransfersAlerts from '../WarehouseTransfers/WarehousesTransfersAlerts';
|
||||
import BranchesAlerts from '../Preferences/Branches/BranchesAlerts';
|
||||
|
||||
export default [
|
||||
...AccountsAlerts,
|
||||
@@ -43,5 +46,8 @@ export default [
|
||||
...RolesAlerts,
|
||||
...CreditNotesAlerts,
|
||||
...VendorCreditNotesAlerts,
|
||||
...TransactionsLockingAlerts
|
||||
...TransactionsLockingAlerts,
|
||||
...WarehousesAlerts,
|
||||
...WarehousesTransfersAlerts,
|
||||
...BranchesAlerts,
|
||||
];
|
||||
|
||||
@@ -3,32 +3,33 @@ import classNames from 'classnames';
|
||||
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { FFormGroup } from '../../../components/Forms';
|
||||
import moment from 'moment';
|
||||
import { Features } from 'common';
|
||||
import {
|
||||
MoneyInputGroup,
|
||||
InputPrependText,
|
||||
CurrencySelectList,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
Row,
|
||||
Col,
|
||||
} from 'components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
inputIntent,
|
||||
} from 'utils';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
import { momentFormatter, tansformDateValue, inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Customer financial panel.
|
||||
*/
|
||||
export default function CustomerFinancialPanel() {
|
||||
const {
|
||||
currencies,
|
||||
customerId
|
||||
} = useCustomerFormContext();
|
||||
const { currencies, customerId, branches } = useCustomerFormContext();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={'tab-panel--financial'}>
|
||||
@@ -62,12 +63,7 @@ export default function CustomerFinancialPanel() {
|
||||
|
||||
{/*------------ Opening balance -----------*/}
|
||||
<FastField name={'opening_balance'}>
|
||||
{({
|
||||
form,
|
||||
field,
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
{({ form, field, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'opening_balance'} />}
|
||||
className={classNames(
|
||||
@@ -92,6 +88,23 @@ export default function CustomerFinancialPanel() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Opening branch -----------*/}
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<FFormGroup
|
||||
label={<T id={'customer.label.opening_branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
inline={true}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</FeatureCan>
|
||||
|
||||
{/*------------ Currency -----------*/}
|
||||
<FastField name={'currency_code'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
@@ -110,7 +123,6 @@ export default function CustomerFinancialPanel() {
|
||||
onCurrencySelected={(currency) => {
|
||||
form.setFieldValue('currency_code', currency.currency_code);
|
||||
}}
|
||||
disabled={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
|
||||
@@ -42,6 +42,7 @@ const Schema = Yup.object().shape({
|
||||
opening_balance: Yup.number().nullable(),
|
||||
currency_code: Yup.string(),
|
||||
opening_balance_at: Yup.date(),
|
||||
opening_balance_branch_id: Yup.string(),
|
||||
});
|
||||
|
||||
export const CreateCustomerForm = Schema;
|
||||
|
||||
@@ -6,15 +6,21 @@ import {
|
||||
useCreateCustomer,
|
||||
useEditCustomer,
|
||||
useContact,
|
||||
useBranches,
|
||||
} from 'hooks/query';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
|
||||
const CustomerFormContext = createContext();
|
||||
|
||||
function CustomerFormProvider({ customerId, ...props }) {
|
||||
function CustomerFormProvider({ query, customerId, ...props }) {
|
||||
const { state } = useLocation();
|
||||
|
||||
const contactId = state?.action;
|
||||
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch customer details.
|
||||
const { data: customer, isLoading: isCustomerLoading } = useCustomer(
|
||||
customerId,
|
||||
@@ -28,6 +34,13 @@ function CustomerFormProvider({ customerId, ...props }) {
|
||||
// Handle fetch Currencies data table
|
||||
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Form submit payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
@@ -38,18 +51,20 @@ function CustomerFormProvider({ customerId, ...props }) {
|
||||
const isNewMode = contactId || !customerId;
|
||||
|
||||
const isFormLoading =
|
||||
isCustomerLoading || isCurrenciesLoading || isContactLoading;
|
||||
isCustomerLoading || isCurrenciesLoading || isBranchesLoading;
|
||||
|
||||
const provider = {
|
||||
customerId,
|
||||
customer,
|
||||
currencies,
|
||||
branches,
|
||||
contactDuplicate,
|
||||
submitPayload,
|
||||
isNewMode,
|
||||
|
||||
isCustomerLoading,
|
||||
isCurrenciesLoading,
|
||||
isBranchesSuccess,
|
||||
isFormLoading,
|
||||
|
||||
setSubmitPayload,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { first } from 'lodash';
|
||||
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
export const defaultInitialValues = {
|
||||
customer_type: 'business',
|
||||
@@ -35,4 +39,20 @@ export const defaultInitialValues = {
|
||||
opening_balance: '',
|
||||
currency_code: '',
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
opening_balance_branch_id: '',
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useCustomerFormContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('opening_balance_branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ const defaultInitialValues = {
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
currency_code:'',
|
||||
subaccount: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -17,12 +17,14 @@ import {
|
||||
Hint,
|
||||
AccountsSelectList,
|
||||
AccountsTypesSelect,
|
||||
CurrencySelect,
|
||||
} from 'components';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
|
||||
import { inputIntent } from 'utils';
|
||||
import { compose } from 'redux';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { FOREIGN_CURRENCY_ACCOUNTS } from '../../../common/accountTypes';
|
||||
import { useAccountDialogContext } from './AccountDialogProvider';
|
||||
|
||||
/**
|
||||
@@ -37,7 +39,7 @@ function AccountFormDialogFields({
|
||||
const accountNameFieldRef = useAutofocus();
|
||||
|
||||
// Account form context.
|
||||
const { accounts, accountsTypes } = useAccountDialogContext();
|
||||
const { accounts, accountsTypes, currencies } = useAccountDialogContext();
|
||||
|
||||
return (
|
||||
<Form>
|
||||
@@ -58,6 +60,7 @@ function AccountFormDialogFields({
|
||||
defaultSelectText={<T id={'select_account_type'} />}
|
||||
onTypeSelected={(accountType) => {
|
||||
form.setFieldValue('account_type', accountType.key);
|
||||
form.setFieldValue('currency_code', '');
|
||||
}}
|
||||
disabled={
|
||||
action === 'edit' ||
|
||||
@@ -143,6 +146,7 @@ function AccountFormDialogFields({
|
||||
)}
|
||||
inline={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="parent_account_id" />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
@@ -159,6 +163,24 @@ function AccountFormDialogFields({
|
||||
</FastField>
|
||||
</If>
|
||||
|
||||
<If condition={FOREIGN_CURRENCY_ACCOUNTS.includes(values.account_type)}>
|
||||
{/*------------ Currency -----------*/}
|
||||
<FastField name={'currency_code'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'currency'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
inline={true}
|
||||
>
|
||||
<CurrencySelect
|
||||
name={'currency_code'}
|
||||
currencies={currencies}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</If>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DialogContent } from 'components';
|
||||
import {
|
||||
useCreateAccount,
|
||||
useAccountsTypes,
|
||||
useCurrencies,
|
||||
useAccount,
|
||||
useAccounts,
|
||||
useEditAccount,
|
||||
@@ -30,16 +31,17 @@ function AccountDialogProvider({
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
// Fetches accounts types.
|
||||
const {
|
||||
data: accountsTypes,
|
||||
isLoading: isAccountsTypesLoading,
|
||||
} = useAccountsTypes();
|
||||
const { data: accountsTypes, isLoading: isAccountsTypesLoading } =
|
||||
useAccountsTypes();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
|
||||
// Handle fetch Currencies data table
|
||||
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
|
||||
|
||||
const isNewMode = !accountId;
|
||||
|
||||
// Provider payload.
|
||||
@@ -49,6 +51,7 @@ function AccountDialogProvider({
|
||||
parentAccountId,
|
||||
action,
|
||||
accountType,
|
||||
currencies,
|
||||
|
||||
createAccountMutate,
|
||||
editAccountMutate,
|
||||
@@ -57,11 +60,15 @@ function AccountDialogProvider({
|
||||
account,
|
||||
|
||||
isAccountsLoading,
|
||||
isNewMode
|
||||
isCurrenciesLoading,
|
||||
isNewMode,
|
||||
};
|
||||
|
||||
const isLoading =
|
||||
isAccountsLoading || isAccountsTypesLoading || isAccountLoading;
|
||||
isAccountsLoading ||
|
||||
isAccountsTypesLoading ||
|
||||
isAccountLoading ||
|
||||
isCurrenciesLoading;
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isLoading}>
|
||||
|
||||
@@ -10,6 +10,13 @@ export const transformApiErrors = (errors) => {
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.NAME.NOT.UNIQUE')) {
|
||||
fields.name = intl.get('account_name_is_already_used');
|
||||
}
|
||||
if (
|
||||
errors.find((e) => e.type === 'ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT')
|
||||
) {
|
||||
fields.parent_account_id = intl.get(
|
||||
'accounts.error.account_currency_not_same_parent_account',
|
||||
);
|
||||
}
|
||||
return fields;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ function BadDebtForm({
|
||||
// Initial form values
|
||||
const initialValues = {
|
||||
...defaultInitialValues,
|
||||
currency_code: base_currency,
|
||||
amount: invoice.due_amount,
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import { useBadDebtContext } from './BadDebtFormProvider';
|
||||
function BadDebtFormFields() {
|
||||
const amountfieldRef = useAutofocus();
|
||||
|
||||
const { accounts } = useBadDebtContext();
|
||||
const { accounts ,invoice } = useBadDebtContext();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
@@ -55,7 +55,7 @@ function BadDebtFormFields() {
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={invoice.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import BranchActivateForm from './BranchActivateForm';
|
||||
import { BranchActivateFormProvider } from './BranchActivateFormProvider';
|
||||
|
||||
export default function BranchActivateDialogContent({
|
||||
// #ownProps
|
||||
dialogName,
|
||||
}) {
|
||||
return (
|
||||
<BranchActivateFormProvider dialogName={dialogName}>
|
||||
<BranchActivateForm />
|
||||
</BranchActivateFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { useBranchActivateContext } from './BranchActivateFormProvider';
|
||||
import BranchActivateFormContent from './BranchActivateFormContent';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Branch activate form.
|
||||
*/
|
||||
function BranchActivateForm({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { activateBranches, dialogName } = useBranchActivateContext();
|
||||
|
||||
// Initial form values
|
||||
const initialValues = {};
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const form = {
|
||||
...values,
|
||||
};
|
||||
setSubmitting(true);
|
||||
// Handle request response success.
|
||||
const onSuccess = (response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('branch_activate.dialog_success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
// Handle request response errors.
|
||||
const onError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors) {
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
activateBranches(form).then(onSuccess).catch(onError);
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={BranchActivateFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(BranchActivateForm);
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Form } from 'formik';
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
import BranchActivateFormFloatingActions from './BranchActivateFormFloatingActions';
|
||||
|
||||
/**
|
||||
* Branch activate form content.
|
||||
*/
|
||||
export default function BranchActivateFormContent() {
|
||||
return (
|
||||
<Form>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<p class="paragraph">
|
||||
{intl.getHTML('branch_activate.dialog_paragraph')}
|
||||
</p>
|
||||
|
||||
<ul class="paragraph list">
|
||||
<li>{intl.get('branch_activate.dialog_paragraph.line_1')}</li>
|
||||
<li>{intl.get('branch_activate.dialog_paragraph.line_2')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<BranchActivateFormFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { Intent, Button, Classes } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useBranchActivateContext } from './BranchActivateFormProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* branch activate form floating actions.
|
||||
*/
|
||||
function BranchActivateFormFloatingActions({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
// branch activate dialog context.
|
||||
const { dialogName } = useBranchActivateContext();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
// Handle close button click.
|
||||
const handleCancelBtnClick = () => {
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '85px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '95px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'branches.activate_button'} />}
|
||||
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(BranchActivateFormFloatingActions);
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import { DialogContent } from 'components';
|
||||
import { useActivateBranches } from 'hooks/query';
|
||||
|
||||
const BranchActivateContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Branch activate form provider.
|
||||
*/
|
||||
function BranchActivateFormProvider({ dialogName, ...props }) {
|
||||
const { mutateAsync: activateBranches, isLoading } = useActivateBranches();
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
activateBranches,
|
||||
dialogName,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isLoading}>
|
||||
<BranchActivateContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useBranchActivateContext = () => React.useContext(BranchActivateContext);
|
||||
|
||||
export { BranchActivateFormProvider, useBranchActivateContext };
|
||||
32
src/containers/Dialogs/BranchActivateDialog/index.js
Normal file
32
src/containers/Dialogs/BranchActivateDialog/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Dialog, DialogSuspense } from 'components';
|
||||
import withDialogRedux from 'components/DialogReduxConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const BranchActivateDialogContent = React.lazy(() =>
|
||||
import('./BranchActivateDialogContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Branch activate dialog.
|
||||
*/
|
||||
function BranchActivateDialog({ dialogName, payload: {}, isOpen }) {
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'branch_activate.dialog.label'} />}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
className={'dialog--branch-activate'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<BranchActivateDialogContent dialogName={dialogName} />
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(BranchActivateDialog);
|
||||
82
src/containers/Dialogs/BranchFormDialog/BranchForm.js
Normal file
82
src/containers/Dialogs/BranchFormDialog/BranchForm.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { CreateBranchFormSchema } from './BranchForm.schema';
|
||||
import { transformErrors } from './utils';
|
||||
|
||||
import BranchFormContent from './BranchFormContent';
|
||||
import { useBranchFormContext } from './BranchFormProvider';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose, transformToForm } from 'utils';
|
||||
|
||||
const defaultInitialValues = {
|
||||
name: '',
|
||||
code: '',
|
||||
address: '',
|
||||
phone_number: '',
|
||||
email: '',
|
||||
website: '',
|
||||
city: '',
|
||||
country: '',
|
||||
};
|
||||
|
||||
function BranchForm({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { dialogName, branch, branchId, createBranchMutate, editBranchMutate } =
|
||||
useBranchFormContext();
|
||||
|
||||
// Initial form values.
|
||||
const initialValues = {
|
||||
...defaultInitialValues,
|
||||
...transformToForm(branch, defaultInitialValues),
|
||||
};
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const form = { ...values };
|
||||
|
||||
// Handle request response success.
|
||||
const onSuccess = (response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('branch.dialog.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
// Handle request response errors.
|
||||
const onError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors) {
|
||||
}
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
if (branchId) {
|
||||
editBranchMutate([branchId, form]).then(onSuccess).catch(onError);
|
||||
} else {
|
||||
createBranchMutate(form).then(onSuccess).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={CreateBranchFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={BranchFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(BranchForm);
|
||||
16
src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js
Normal file
16
src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as Yup from 'yup';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DATATYPES_LENGTH } from 'common/dataTypes';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
name: Yup.string().required().label(intl.get('branch_name')),
|
||||
code: Yup.string().trim().min(0).max(DATATYPES_LENGTH.STRING),
|
||||
address: Yup.string().trim(),
|
||||
city: Yup.string().trim(),
|
||||
country: Yup.string().trim(),
|
||||
website: Yup.string().url().nullable(),
|
||||
phone_number: Yup.number(),
|
||||
email: Yup.string().email().nullable(),
|
||||
});
|
||||
|
||||
export const CreateBranchFormSchema = Schema;
|
||||
17
src/containers/Dialogs/BranchFormDialog/BranchFormContent.js
Normal file
17
src/containers/Dialogs/BranchFormDialog/BranchFormContent.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { Form } from 'formik';
|
||||
|
||||
import BranchFormFields from './BranchFormFields';
|
||||
import BranchFormFloatingActions from './BranchFormFloatingActions';
|
||||
|
||||
/**
|
||||
* Branch form content.
|
||||
*/
|
||||
export default function BranchFormContent() {
|
||||
return (
|
||||
<Form>
|
||||
<BranchFormFields />
|
||||
<BranchFormFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
import '../../../style/pages/Branches/BranchFormDialog.scss';
|
||||
|
||||
import { BranchFormProvider } from './BranchFormProvider';
|
||||
import BranchForm from './BranchForm';
|
||||
|
||||
/**
|
||||
* Branch form dialog content.
|
||||
*/
|
||||
export default function BranchFormDialogContent({
|
||||
// #ownProps
|
||||
dialogName,
|
||||
branchId,
|
||||
}) {
|
||||
return (
|
||||
<BranchFormProvider branchId={branchId} dialogName={dialogName}>
|
||||
<BranchForm />
|
||||
</BranchFormProvider>
|
||||
);
|
||||
}
|
||||
153
src/containers/Dialogs/BranchFormDialog/BranchFormFields.js
Normal file
153
src/containers/Dialogs/BranchFormDialog/BranchFormFields.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FastField, ErrorMessage, Field } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
ControlGroup,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { inputIntent } from 'utils';
|
||||
import { FieldRequiredHint, Col, Row, FormattedMessage as T } from 'components';
|
||||
import { useBranchFormContext } from './BranchFormProvider';
|
||||
|
||||
/**
|
||||
* Branch form dialog fields.
|
||||
*/
|
||||
function BranchFormFields() {
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------ Branch Name -----------*/}
|
||||
<FastField name={'name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'branch.dialog.label.branch_name'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="branch_name" />}
|
||||
className={'form-group--branch_name'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/*------------ Branch Code -----------*/}
|
||||
<FastField name={'code'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'branch.dialog.label.branch_code'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="code" />}
|
||||
className={'form-group--branch_name'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Branch Address -----------*/}
|
||||
<FastField name={'address'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={intl.get('branch.dialog.label.branch_address')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="address" />}
|
||||
className={'form-group--branch_address'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
placeholder={intl.get('branch.dialog.label.address_1')}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<BranchAddressWrap>
|
||||
{/*------------ Branch Address City & Country-----------*/}
|
||||
<FormGroup
|
||||
inline={true}
|
||||
className={'form-group--branch_address'}
|
||||
helperText={<ErrorMessage name="branch_address_2" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<FastField name={'city'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
placeholder={intl.get('branch.dialog.label.city')}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'country'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
placeholder={intl.get('branch.dialog.label.country')}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
</BranchAddressWrap>
|
||||
|
||||
{/*------------ Phone Number -----------*/}
|
||||
<FastField name={'phone_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={intl.get('branch.dialog.label.phone_number')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="phone_number" />}
|
||||
className={'form-group--phone_number'}
|
||||
>
|
||||
<InputGroup placeholder={'https://'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Email -----------*/}
|
||||
<FastField name={'email'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={intl.get('branch.dialog.label.email')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="email" />}
|
||||
className={'form-group--email'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Website -----------*/}
|
||||
<FastField name={'website'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={intl.get('branch.dialog.label.website')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="email" />}
|
||||
className={'form-group--website'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BranchFormFields;
|
||||
|
||||
const BranchAddressWrap = styled.div`
|
||||
margin-left: 160px;
|
||||
`;
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Intent, Button, Classes } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useBranchFormContext } from './BranchFormProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Branch form floating actions.
|
||||
*/
|
||||
function BranchFormFloatingActions({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
const { dialogName } = useBranchFormContext();
|
||||
|
||||
// Handle close button click.
|
||||
const handleCancelBtnClick = () => {
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '85px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '95px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'save'} />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(BranchFormFloatingActions);
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { useCreateBranch, useEditBranch, useBranch } from 'hooks/query';
|
||||
|
||||
const BranchFormContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Branch form dialog provider.
|
||||
*/
|
||||
function BranchFormProvider({ dialogName, branchId, ...props }) {
|
||||
// Create and edit warehouse mutations.
|
||||
const { mutateAsync: createBranchMutate } = useCreateBranch();
|
||||
const { mutateAsync: editBranchMutate } = useEditBranch();
|
||||
|
||||
// Handle fetch branch detail.
|
||||
const { data: branch, isLoading: isBranchLoading } = useBranch(branchId, {
|
||||
enabled: !!branchId,
|
||||
});
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
dialogName,
|
||||
branch,
|
||||
branchId,
|
||||
createBranchMutate,
|
||||
editBranchMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isBranchLoading}>
|
||||
<BranchFormContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
const useBranchFormContext = () => React.useContext(BranchFormContext);
|
||||
|
||||
export { BranchFormProvider, useBranchFormContext };
|
||||
41
src/containers/Dialogs/BranchFormDialog/index.js
Normal file
41
src/containers/Dialogs/BranchFormDialog/index.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Dialog, DialogSuspense } from 'components';
|
||||
import withDialogRedux from 'components/DialogReduxConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const BranchFormDialogContent = React.lazy(() =>
|
||||
import('./BranchFormDialogContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Branch form form dialog.
|
||||
*/
|
||||
function BranchFormDialog({
|
||||
dialogName,
|
||||
payload: { branchId, action },
|
||||
isOpen,
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={
|
||||
action === 'edit' ? (
|
||||
<T id={'branch.dialog.label_edit_branch'} />
|
||||
) : (
|
||||
<T id={'branch.dialog.label_new_branch'} />
|
||||
)
|
||||
}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
className={'dialog--branch-form'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<BranchFormDialogContent dialogName={dialogName} branchId={branchId} />
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogRedux())(BranchFormDialog);
|
||||
14
src/containers/Dialogs/BranchFormDialog/utils.js
Normal file
14
src/containers/Dialogs/BranchFormDialog/utils.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
|
||||
/**
|
||||
* Transformes the response errors types.
|
||||
*/
|
||||
export const transformErrors = (errors, { setErrors }) => {
|
||||
|
||||
if (errors.find((error) => error.type === 'BRANCH_CODE_NOT_UNIQUE')) {
|
||||
setErrors({
|
||||
code: intl.get('branche.error.warehouse_code_not_unique'),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/pages/CustomerOpeningBalance/CustomerOpeningBalance.scss';
|
||||
|
||||
import CustomerOpeningBalanceForm from './CustomerOpeningBalanceForm';
|
||||
import { CustomerOpeningBalanceFormProvider } from './CustomerOpeningBalanceFormProvider';
|
||||
|
||||
/**
|
||||
* Customer opening balance dialog content.
|
||||
* @returns
|
||||
*/
|
||||
export default function CustomerOpeningBalanceDialogContent({
|
||||
// #ownProps
|
||||
dialogName,
|
||||
customerId,
|
||||
}) {
|
||||
return (
|
||||
<CustomerOpeningBalanceFormProvider
|
||||
customerId={customerId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<CustomerOpeningBalanceForm />
|
||||
</CustomerOpeningBalanceFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import React from 'react';
|
||||
import { Classes, Position, FormGroup, ControlGroup } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { isEqual } from 'lodash';
|
||||
import { FastField, useFormikContext } from 'formik';
|
||||
import { momentFormatter, tansformDateValue, handleDateChange } from 'utils';
|
||||
import { Features } from 'common';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
If,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
InputPrependText,
|
||||
} from 'components';
|
||||
import { FMoneyInputGroup, FFormGroup } from '../../../components/Forms';
|
||||
|
||||
import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormProvider';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Customer Opening balance fields.
|
||||
* @returns
|
||||
*/
|
||||
function CustomerOpeningBalanceFields({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
// Formik context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const { branches, customer } = useCustomerOpeningBalanceContext();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------ Opening balance -----------*/}
|
||||
<FFormGroup
|
||||
name={'opening_balance'}
|
||||
label={<T id={'customer_opening_balance.label.opening_balance'} />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={customer.currency_code} />
|
||||
<FMoneyInputGroup
|
||||
name={'opening_balance'}
|
||||
allowDecimals={true}
|
||||
allowNegativeValue={true}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Opening balance at -----------*/}
|
||||
<FastField name={'opening_balance_at'}>
|
||||
{({ form, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<T id={'customer_opening_balance.label.opening_balance_at'} />
|
||||
}
|
||||
className={Classes.FILL}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('opening_balance_at', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, customer.currency_code)}>
|
||||
{/*------------ Opening balance exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'opening_balance_exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={customer.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.opening_balance_at}
|
||||
exchangeRate={values.opening_balance_exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
|
||||
{/*------------ Opening balance branch id -----------*/}
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<FFormGroup
|
||||
label={<T id={'branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</FeatureCan>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withCurrentOrganization())(CustomerOpeningBalanceFields);
|
||||
@@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { CreateCustomerOpeningBalanceFormSchema } from './CustomerOpeningBalanceForm.schema';
|
||||
import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormProvider';
|
||||
|
||||
import CustomerOpeningBalanceFormContent from './CustomerOpeningBalanceFormContent';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const defaultInitialValues = {
|
||||
opening_balance: '0',
|
||||
opening_balance_branch_id: '',
|
||||
opening_balance_exchange_rate: 1,
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
};
|
||||
|
||||
/**
|
||||
* Customer Opening balance form.
|
||||
* @returns
|
||||
*/
|
||||
function CustomerOpeningBalanceForm({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { dialogName, customer, editCustomerOpeningBalanceMutate } =
|
||||
useCustomerOpeningBalanceContext();
|
||||
|
||||
// Initial form values
|
||||
const initialValues = {
|
||||
...defaultInitialValues,
|
||||
...customer,
|
||||
opening_balance: defaultTo(customer.opening_balance, ''),
|
||||
};
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const formValues = {
|
||||
...values,
|
||||
};
|
||||
|
||||
// Handle request response success.
|
||||
const onSuccess = (response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('customer_opening_balance.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
// Handle request response errors.
|
||||
const onError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors) {
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
editCustomerOpeningBalanceMutate([customer.id, formValues])
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={CreateCustomerOpeningBalanceFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={CustomerOpeningBalanceFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(CustomerOpeningBalanceForm);
|
||||
@@ -0,0 +1,10 @@
|
||||
import * as Yup from 'yup';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
opening_balance_branch_id: Yup.string(),
|
||||
opening_balance: Yup.number().nullable(),
|
||||
opening_balance_at: Yup.date(),
|
||||
opening_balance_exchange_rate: Yup.number(),
|
||||
});
|
||||
|
||||
export const CreateCustomerOpeningBalanceFormSchema = Schema;
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { Form } from 'formik';
|
||||
|
||||
import CustomerOpeningBalanceFields from './CustomerOpeningBalanceFields';
|
||||
import CustomerOpeningBalanceFormFloatingActions from './CustomerOpeningBalanceFormFloatingActions';
|
||||
|
||||
/**
|
||||
* Customer Opening balance form content.
|
||||
* @returns
|
||||
*/
|
||||
export default function CustomerOpeningBalanceFormContent() {
|
||||
return (
|
||||
<Form>
|
||||
<CustomerOpeningBalanceFields />
|
||||
<CustomerOpeningBalanceFormFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import { Intent, Button, Classes } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
/**
|
||||
* Customer Opening balance floating actions.
|
||||
* @returns
|
||||
*/
|
||||
function CustomerOpeningBalanceFormFloatingActions({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
// dialog context.
|
||||
const { dialogName } = useCustomerOpeningBalanceContext();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
// Handle close button click.
|
||||
const handleCancelBtnClick = () => {
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '75px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'edit'} />}
|
||||
</Button>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '75px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(
|
||||
CustomerOpeningBalanceFormFloatingActions,
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import {
|
||||
useBranches,
|
||||
useCustomer,
|
||||
useEditCustomerOpeningBalance,
|
||||
} from 'hooks/query';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import { Features } from 'common';
|
||||
import { transfromCustomertoForm } from './utils';
|
||||
|
||||
const CustomerOpeningBalanceContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Customer opening balance provider.
|
||||
* @returns
|
||||
*/
|
||||
function CustomerOpeningBalanceFormProvider({
|
||||
query,
|
||||
customerId,
|
||||
dialogName,
|
||||
...props
|
||||
}) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
const { mutateAsync: editCustomerOpeningBalanceMutate } =
|
||||
useEditCustomerOpeningBalance();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Handle fetch customer details.
|
||||
const { data: customer, isLoading: isCustomerLoading } = useCustomer(
|
||||
customerId,
|
||||
{ enabled: !!customerId },
|
||||
);
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
branches,
|
||||
customer: transfromCustomertoForm(customer),
|
||||
|
||||
isBranchesSuccess,
|
||||
isBranchesLoading,
|
||||
dialogName,
|
||||
editCustomerOpeningBalanceMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isBranchesLoading || isCustomerLoading}>
|
||||
<CustomerOpeningBalanceContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useCustomerOpeningBalanceContext = () =>
|
||||
React.useContext(CustomerOpeningBalanceContext);
|
||||
|
||||
export { CustomerOpeningBalanceFormProvider, useCustomerOpeningBalanceContext };
|
||||
40
src/containers/Dialogs/CustomerOpeningBalanceDialog/index.js
Normal file
40
src/containers/Dialogs/CustomerOpeningBalanceDialog/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Dialog, DialogSuspense } from 'components';
|
||||
import withDialogRedux from 'components/DialogReduxConnect';
|
||||
import { compose } from 'redux';
|
||||
|
||||
const CustomerOpeningBalanceDialogContent = React.lazy(() =>
|
||||
import('./CustomerOpeningBalanceDialogContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Customer opening balance dialog.
|
||||
* @returns
|
||||
*/
|
||||
function CustomerOpeningBalanceDialog({
|
||||
dialogName,
|
||||
payload: { customerId },
|
||||
isOpen,
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'customer_opening_balance.label'} />}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
className={'dialog--customer-opening-balance'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<CustomerOpeningBalanceDialogContent
|
||||
customerId={customerId}
|
||||
dialogName={dialogName}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(CustomerOpeningBalanceDialog);
|
||||
31
src/containers/Dialogs/CustomerOpeningBalanceDialog/utils.js
Normal file
31
src/containers/Dialogs/CustomerOpeningBalanceDialog/utils.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { first, pick } from 'lodash';
|
||||
|
||||
import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormProvider';
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useCustomerOpeningBalanceContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('opening_balance_branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
export function transfromCustomertoForm(values) {
|
||||
return {
|
||||
...pick(values, [
|
||||
'id',
|
||||
'opening_balance',
|
||||
'opening_balance_exchange_rate',
|
||||
'currency_code',
|
||||
]),
|
||||
};
|
||||
}
|
||||
@@ -27,6 +27,8 @@ const defaultInitialValues = {
|
||||
reference_no: '',
|
||||
quantity_on_hand: '',
|
||||
publish: '',
|
||||
branch_id: '',
|
||||
warehouse_id: '',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -36,13 +38,8 @@ function InventoryAdjustmentForm({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const {
|
||||
dialogName,
|
||||
item,
|
||||
itemId,
|
||||
submitPayload,
|
||||
createInventoryAdjMutate,
|
||||
} = useInventoryAdjContext();
|
||||
const { dialogName, item, itemId, submitPayload, createInventoryAdjMutate } =
|
||||
useInventoryAdjContext();
|
||||
|
||||
// Initial form values.
|
||||
const initialValues = {
|
||||
@@ -63,7 +60,9 @@ function InventoryAdjustmentForm({
|
||||
closeDialog(dialogName);
|
||||
|
||||
AppToaster.show({
|
||||
message: intl.get('the_adjustment_transaction_has_been_created_successfully'),
|
||||
message: intl.get(
|
||||
'the_adjustment_transaction_has_been_created_successfully',
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
|
||||
@@ -23,6 +23,8 @@ const Schema = Yup.object().shape({
|
||||
reference_no: Yup.string(),
|
||||
new_quantity: Yup.number().required(),
|
||||
publish: Yup.boolean(),
|
||||
branch_id: Yup.string(),
|
||||
warehouse_id: Yup.string(),
|
||||
});
|
||||
|
||||
export const CreateInventoryAdjustmentFormSchema = Schema;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { FastField, ErrorMessage, Field } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
@@ -12,7 +13,17 @@ import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { ListSelect, FieldRequiredHint, Col, Row } from 'components';
|
||||
import {
|
||||
ListSelect,
|
||||
FieldRequiredHint,
|
||||
Col,
|
||||
Row,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
WarehouseSelect,
|
||||
BranchSelectButton,
|
||||
WarehouseSelectButton,
|
||||
} from 'components';
|
||||
import {
|
||||
inputIntent,
|
||||
momentFormatter,
|
||||
@@ -21,26 +32,76 @@ import {
|
||||
toSafeNumber,
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Features } from 'common';
|
||||
import adjustmentType from 'common/adjustmentType';
|
||||
|
||||
import AccountsSuggestField from 'components/AccountsSuggestField';
|
||||
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
|
||||
import { diffQuantity } from './utils';
|
||||
import {
|
||||
diffQuantity,
|
||||
useSetPrimaryBranchToForm,
|
||||
useSetPrimaryWarehouseToForm,
|
||||
} from './utils';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import InventoryAdjustmentQuantityFields from './InventoryAdjustmentQuantityFields';
|
||||
|
||||
/**
|
||||
* Inventory adjustment form dialogs fields.
|
||||
*/
|
||||
export default function InventoryAdjustmentFormDialogFields() {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
|
||||
const dateFieldRef = useAutofocus();
|
||||
|
||||
// Inventory adjustment dialog context.
|
||||
const { accounts } = useInventoryAdjContext();
|
||||
const { accounts, branches, warehouses } = useInventoryAdjContext();
|
||||
|
||||
// Intl context.
|
||||
// Sets the primary warehouse to form.
|
||||
useSetPrimaryWarehouseToForm();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Row>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</FeatureCan>
|
||||
<FeatureCan feature={Features.Warehouses}>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'warehouse'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<WarehouseSelect
|
||||
name={'warehouse_id'}
|
||||
warehouses={warehouses}
|
||||
input={WarehouseSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</FeatureCan>
|
||||
</Row>
|
||||
|
||||
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
|
||||
<FeatureRowDivider />
|
||||
)}
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -173,3 +234,9 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FeatureRowDivider = styled.div`
|
||||
height: 2px;
|
||||
background: #e9e9e9;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React, { useState, createContext } from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useItem,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useWarehouses,
|
||||
useCreateInventoryAdjustment,
|
||||
} from 'hooks/query';
|
||||
|
||||
@@ -12,29 +16,59 @@ const InventoryAdjustmentContext = createContext();
|
||||
* Inventory adjustment dialog provider.
|
||||
*/
|
||||
function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isWarehouseFeatureCan = featureCan(Features.Warehouses);
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
||||
|
||||
// Fetches the item details.
|
||||
const { isFetching: isItemLoading, data: item } = useItem(itemId);
|
||||
|
||||
// Fetch warehouses list.
|
||||
const {
|
||||
mutateAsync: createInventoryAdjMutate,
|
||||
} = useCreateInventoryAdjustment();
|
||||
data: warehouses,
|
||||
isLoading: isWarehouesLoading,
|
||||
isSuccess: isWarehousesSuccess,
|
||||
} = useWarehouses({}, { enabled: isWarehouseFeatureCan });
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches({}, { enabled: isBranchFeatureCan });
|
||||
|
||||
const { mutateAsync: createInventoryAdjMutate } =
|
||||
useCreateInventoryAdjustment();
|
||||
|
||||
// Submit payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
// Determines whether the warehouse and branches are loading.
|
||||
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
itemId,
|
||||
isAccountsLoading,
|
||||
accounts,
|
||||
isItemLoading,
|
||||
item,
|
||||
submitPayload,
|
||||
itemId,
|
||||
branches,
|
||||
warehouses,
|
||||
accounts,
|
||||
|
||||
dialogName,
|
||||
|
||||
submitPayload,
|
||||
|
||||
isBranchesSuccess,
|
||||
isWarehousesSuccess,
|
||||
isAccountsLoading,
|
||||
isItemLoading,
|
||||
isFeatureLoading,
|
||||
isWarehouesLoading,
|
||||
isBranchesLoading,
|
||||
|
||||
createInventoryAdjMutate,
|
||||
setSubmitPayload,
|
||||
};
|
||||
@@ -46,6 +80,7 @@ function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) {
|
||||
);
|
||||
}
|
||||
|
||||
const useInventoryAdjContext = () => React.useContext(InventoryAdjustmentContext);
|
||||
const useInventoryAdjContext = () =>
|
||||
React.useContext(InventoryAdjustmentContext);
|
||||
|
||||
export { InventoryAdjustmentFormProvider, useInventoryAdjContext };
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
|
||||
import { first } from 'lodash';
|
||||
|
||||
export const decrementQuantity = (newQuantity, quantityOnHand) => {
|
||||
return quantityOnHand - newQuantity;
|
||||
@@ -12,3 +16,34 @@ export const diffQuantity = (newQuantity, quantityOnHand, type) => {
|
||||
? decrementQuantity(newQuantity, quantityOnHand)
|
||||
: incrementQuantity(newQuantity, quantityOnHand);
|
||||
};
|
||||
|
||||
export const useSetPrimaryWarehouseToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { warehouses, isWarehousesSuccess } = useInventoryAdjContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isWarehousesSuccess) {
|
||||
const primaryWarehouse =
|
||||
warehouses.find((b) => b.primary) || first(warehouses);
|
||||
|
||||
if (primaryWarehouse) {
|
||||
setFieldValue('warehouse_id', primaryWarehouse.id);
|
||||
}
|
||||
}
|
||||
}, [isWarehousesSuccess, setFieldValue, warehouses]);
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useInventoryAdjContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DialogContent } from 'components';
|
||||
import { useSaveSettings, useSettingsManualJournals } from 'hooks/query';
|
||||
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
transformSettingsToForm,
|
||||
} from 'containers/JournalNumber/utils';
|
||||
|
||||
import 'style/pages/ManualJournal/JournalNumberDialog.scss'
|
||||
import 'style/pages/ManualJournal/JournalNumberDialog.scss';
|
||||
|
||||
/**
|
||||
* Journal number dialog's content.
|
||||
@@ -28,16 +29,14 @@ function JournalNumberDialogContent({
|
||||
|
||||
// #ownProps
|
||||
onConfirm,
|
||||
initialValues
|
||||
initialValues,
|
||||
}) {
|
||||
const { isLoading: isSettingsLoading } = useSettingsManualJournals();
|
||||
const { mutateAsync: saveSettingsMutate } = useSaveSettings();
|
||||
const [referenceFormValues, setReferenceFormValues] = React.useState(null);
|
||||
|
||||
// Handle the form submit.
|
||||
// Handle the form submit.
|
||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||
// Transformes the form values to settings to save it.
|
||||
const options = transformFormToSettings(values, 'manual_journals');
|
||||
|
||||
// Handle success.
|
||||
const handleSuccess = () => {
|
||||
setSubmitting(false);
|
||||
@@ -52,6 +51,9 @@ function JournalNumberDialogContent({
|
||||
handleSuccess();
|
||||
return;
|
||||
}
|
||||
// Transformes the form values to settings to save it.
|
||||
const options = transformFormToSettings(values, 'manual_journals');
|
||||
|
||||
saveSettingsMutate({ options }).then(handleSuccess).catch(handleErrors);
|
||||
};
|
||||
|
||||
@@ -59,6 +61,17 @@ function JournalNumberDialogContent({
|
||||
closeDialog('journal-number-form');
|
||||
}, [closeDialog]);
|
||||
|
||||
// Handle form change.
|
||||
const handleChange = (values) => {
|
||||
setReferenceFormValues(values);
|
||||
};
|
||||
|
||||
// Description.
|
||||
const description =
|
||||
referenceFormValues?.incrementMode === 'auto'
|
||||
? intl.get('manual_journals.auto_increment.auto')
|
||||
: intl.get('manual_journals.auto_increment.manually');
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isSettingsLoading}>
|
||||
<ReferenceNumberForm
|
||||
@@ -71,7 +84,9 @@ function JournalNumberDialogContent({
|
||||
...initialValues,
|
||||
}}
|
||||
onSubmit={handleSubmitForm}
|
||||
description={description}
|
||||
onClose={handleClose}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</DialogContent>
|
||||
);
|
||||
@@ -84,4 +99,4 @@ export default compose(
|
||||
numberPrefix: manualJournalsSettings?.numberPrefix,
|
||||
autoIncrement: manualJournalsSettings?.autoIncrement,
|
||||
})),
|
||||
)(JournalNumberDialogContent);
|
||||
)(JournalNumberDialogContent);
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useCreateCashflowTransaction,
|
||||
useAccount,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCashflowAccounts,
|
||||
useSettingCashFlow,
|
||||
} from 'hooks/query';
|
||||
@@ -18,9 +22,24 @@ function MoneyInDialogProvider({
|
||||
dialogName,
|
||||
...props
|
||||
}) {
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches({}, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Fetch cash flow list .
|
||||
const { data: cashflowAccounts, isLoading: isCashFlowAccountsLoading } =
|
||||
useCashflowAccounts({}, { keepPreviousData: true });
|
||||
@@ -37,9 +56,12 @@ function MoneyInDialogProvider({
|
||||
// provider.
|
||||
const provider = {
|
||||
accounts,
|
||||
account,
|
||||
branches,
|
||||
accountId,
|
||||
accountType,
|
||||
isAccountsLoading,
|
||||
isBranchesSuccess,
|
||||
|
||||
cashflowAccounts,
|
||||
|
||||
@@ -53,7 +75,10 @@ function MoneyInDialogProvider({
|
||||
return (
|
||||
<DialogContent
|
||||
isLoading={
|
||||
isAccountsLoading || isCashFlowAccountsLoading || isSettingsLoading
|
||||
isAccountsLoading ||
|
||||
isCashFlowAccountsLoading ||
|
||||
isBranchesLoading ||
|
||||
isSettingsLoading
|
||||
}
|
||||
>
|
||||
<MoneyInDialogContent.Provider value={provider} {...props} />
|
||||
|
||||
@@ -48,7 +48,7 @@ function MoneyInFloatingActions({
|
||||
>
|
||||
<T id={'close'} />
|
||||
</Button>
|
||||
<Button
|
||||
{/* <Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting && !submitPayload.publish}
|
||||
style={{ minWidth: '75px' }}
|
||||
@@ -56,7 +56,7 @@ function MoneyInFloatingActions({
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
>
|
||||
{<T id={'save_as_draft'} />}
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
|
||||
@@ -28,8 +28,11 @@ const defaultInitialValues = {
|
||||
reference_no: '',
|
||||
cashflow_account_id: '',
|
||||
credit_account_id: '',
|
||||
currency_code: '',
|
||||
description: '',
|
||||
branch_id: '',
|
||||
publish: '',
|
||||
exchange_rate: 1,
|
||||
};
|
||||
|
||||
function MoneyInForm({
|
||||
|
||||
@@ -10,11 +10,14 @@ const Schema = Yup.object().shape({
|
||||
reference_no: Yup.string(),
|
||||
credit_account_id: Yup.number().required(),
|
||||
cashflow_account_id: Yup.string().required(),
|
||||
branch_id: Yup.string(),
|
||||
exchange_rate: Yup.number(),
|
||||
|
||||
description: Yup.string()
|
||||
.min(3)
|
||||
.max(DATATYPES_LENGTH.TEXT)
|
||||
.label(intl.get('description')),
|
||||
publish: Yup.boolean(),
|
||||
publish: Yup.boolean(),
|
||||
});
|
||||
|
||||
export const CreateMoneyInFormSchema = Schema;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
TextArea,
|
||||
Position,
|
||||
ControlGroup,
|
||||
Button,
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
@@ -18,7 +19,12 @@ import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
InputPrependButton,
|
||||
ExchangeRateMutedField,
|
||||
} from 'components';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
@@ -32,8 +38,14 @@ import {
|
||||
compose,
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Features } from 'common';
|
||||
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -50,10 +62,14 @@ function OtherIncomeFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money in dialog context.
|
||||
const { accounts } = useMoneyInDailogContext();
|
||||
const { accounts, account, branches } = useMoneyInDailogContext();
|
||||
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
const isForeigAccount = useForeignAccount();
|
||||
|
||||
// Handle tranaction number changing.
|
||||
const handleTransactionNumberChange = () => {
|
||||
openDialog('transaction-number-form');
|
||||
@@ -79,8 +95,30 @@ function OtherIncomeFormFields({
|
||||
transactionNextNumber,
|
||||
);
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -165,7 +203,7 @@ function OtherIncomeFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -181,6 +219,17 @@ function OtherIncomeFormFields({
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={account.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ other income account -----------*/}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
AccountsSuggestField,
|
||||
@@ -18,7 +19,12 @@ import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
InputPrependButton,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
} from 'components';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
@@ -30,9 +36,15 @@ import {
|
||||
handleDateChange,
|
||||
compose,
|
||||
} from 'utils';
|
||||
import { Features } from 'common';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -50,10 +62,14 @@ function OwnerContributionFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money in dialog context.
|
||||
const { accounts } = useMoneyInDailogContext();
|
||||
const { accounts, account, branches } = useMoneyInDailogContext();
|
||||
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
const isForeigAccount = useForeignAccount();
|
||||
|
||||
// Handle tranaction number changing.
|
||||
const handleTransactionNumberChange = () => {
|
||||
openDialog('transaction-number-form');
|
||||
@@ -79,8 +95,29 @@ function OwnerContributionFormFields({
|
||||
transactionNextNumber,
|
||||
);
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -151,7 +188,7 @@ function OwnerContributionFormFields({
|
||||
</Col>
|
||||
</Row>
|
||||
{/*------------ amount -----------*/}
|
||||
<FastField name={'amount'}>
|
||||
<Field name={'amount'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
@@ -165,7 +202,7 @@ function OwnerContributionFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account?.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -179,8 +216,18 @@ function OwnerContributionFormFields({
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
</Field>
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={account.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ equity account -----------*/}
|
||||
@@ -195,9 +242,10 @@ function OwnerContributionFormFields({
|
||||
>
|
||||
<AccountsSuggestField
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('credit_account_id', id)
|
||||
}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('credit_account_id', account.id);
|
||||
form.setFieldValue('currency_code', account.currency_code);
|
||||
}}
|
||||
filterByTypes={ACCOUNT_TYPE.EQUITY}
|
||||
inputProps={{
|
||||
intent: inputIntent({ error, touched }),
|
||||
@@ -226,7 +274,6 @@ function OwnerContributionFormFields({
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/*------------ description -----------*/}
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -18,8 +18,14 @@ import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
InputPrependButton,
|
||||
ExchangeRateMutedField,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
} from 'components';
|
||||
import { Features } from 'common';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
@@ -33,7 +39,12 @@ import {
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -50,10 +61,13 @@ function TransferFromAccountFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money in dialog context.
|
||||
const { accounts } = useMoneyInDailogContext();
|
||||
const { accounts, account, branches } = useMoneyInDailogContext();
|
||||
|
||||
const isForeigAccount = useForeignAccount();
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
const { values } = useFormikContext();
|
||||
|
||||
// Handle tranaction number changing.
|
||||
const handleTransactionNumberChange = () => {
|
||||
openDialog('transaction-number-form');
|
||||
@@ -73,6 +87,9 @@ function TransferFromAccountFormFields({
|
||||
}
|
||||
};
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
// Syncs transaction number settings with form.
|
||||
useObserveTransactionNoSettings(
|
||||
transactionNumberPrefix,
|
||||
@@ -80,6 +97,24 @@ function TransferFromAccountFormFields({
|
||||
);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -164,7 +199,7 @@ function TransferFromAccountFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -179,7 +214,17 @@ function TransferFromAccountFormFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={account.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ transfer from account -----------*/}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { transactionNumber } from 'utils';
|
||||
import { isEqual, isUndefined, isNull, first } from 'lodash';
|
||||
|
||||
import { useMoneyInDailogContext } from './MoneyInDialogProvider';
|
||||
|
||||
export const useObserveTransactionNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
@@ -10,3 +14,34 @@ export const useObserveTransactionNoSettings = (prefix, nextNumber) => {
|
||||
setFieldValue('transacttion_numner', TransactionNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useMoneyInDailogContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
export const useForeignAccount = () => {
|
||||
const { values } = useFormikContext();
|
||||
const { account } = useMoneyInDailogContext();
|
||||
|
||||
return (
|
||||
!isEqual(account.currency_code, values.currency_code) &&
|
||||
!isNull(account.currency_code)
|
||||
);
|
||||
};
|
||||
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useAccount,
|
||||
useBranches,
|
||||
useCreateCashflowTransaction,
|
||||
useCashflowAccounts,
|
||||
useSettingCashFlow,
|
||||
@@ -13,9 +17,25 @@ const MoneyInDialogContent = React.createContext();
|
||||
* Money out dialog provider.
|
||||
*/
|
||||
function MoneyOutProvider({ accountId, accountType, dialogName, ...props }) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches({}, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Fetch cash flow list .
|
||||
const { data: cashflowAccounts, isLoading: isCashFlowAccountsLoading } =
|
||||
useCashflowAccounts({}, { keepPreviousData: true });
|
||||
@@ -32,9 +52,12 @@ function MoneyOutProvider({ accountId, accountType, dialogName, ...props }) {
|
||||
// provider.
|
||||
const provider = {
|
||||
accounts,
|
||||
account,
|
||||
accountId,
|
||||
accountType,
|
||||
branches,
|
||||
isAccountsLoading,
|
||||
isBranchesSuccess,
|
||||
|
||||
cashflowAccounts,
|
||||
|
||||
@@ -48,7 +71,10 @@ function MoneyOutProvider({ accountId, accountType, dialogName, ...props }) {
|
||||
return (
|
||||
<DialogContent
|
||||
isLoading={
|
||||
isAccountsLoading || isCashFlowAccountsLoading || isSettingsLoading
|
||||
isAccountsLoading ||
|
||||
isCashFlowAccountsLoading ||
|
||||
isBranchesLoading ||
|
||||
isSettingsLoading
|
||||
}
|
||||
>
|
||||
<MoneyInDialogContent.Provider value={provider} {...props} />
|
||||
|
||||
@@ -50,7 +50,7 @@ function MoneyOutFloatingActions({
|
||||
>
|
||||
<T id={'close'} />
|
||||
</Button>
|
||||
<Button
|
||||
{/* <Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting && !submitPayload.publish}
|
||||
style={{ minWidth: '75px' }}
|
||||
@@ -58,7 +58,7 @@ function MoneyOutFloatingActions({
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
>
|
||||
{<T id={'save_as_draft'} />}
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
|
||||
@@ -30,6 +30,7 @@ const defaultInitialValues = {
|
||||
credit_account_id: '',
|
||||
description: '',
|
||||
publish: '',
|
||||
exchange_rate: 1,
|
||||
};
|
||||
|
||||
function MoneyOutForm({
|
||||
|
||||
@@ -10,11 +10,13 @@ const Schema = Yup.object().shape({
|
||||
reference_no: Yup.string(),
|
||||
credit_account_id: Yup.number().required(),
|
||||
cashflow_account_id: Yup.string().required(),
|
||||
branch_id: Yup.string(),
|
||||
exchange_rate: Yup.number(),
|
||||
description: Yup.string()
|
||||
.min(3)
|
||||
.max(DATATYPES_LENGTH.TEXT)
|
||||
.label(intl.get('description')),
|
||||
publish: Yup.boolean(),
|
||||
publish: Yup.boolean(),
|
||||
});
|
||||
|
||||
export const CreateMoneyOutSchema = Schema;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -18,11 +18,17 @@ import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
InputPrependButton,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
ExchangeRateMutedField,
|
||||
} from 'components';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { Features } from 'common';
|
||||
|
||||
import {
|
||||
inputIntent,
|
||||
@@ -33,7 +39,12 @@ import {
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -50,7 +61,10 @@ function OtherExpnseFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money in dialog context.
|
||||
const { accounts } = useMoneyOutDialogContext();
|
||||
const { accounts, account, branches } = useMoneyOutDialogContext();
|
||||
|
||||
const isForeigAccount = useForeignAccount();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
@@ -79,8 +93,29 @@ function OtherExpnseFormFields({
|
||||
transactionNextNumber,
|
||||
);
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -165,7 +200,7 @@ function OtherExpnseFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -180,7 +215,17 @@ function OtherExpnseFormFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={account.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ other expense account -----------*/}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -17,12 +17,18 @@ import {
|
||||
FieldRequiredHint,
|
||||
InputPrependButton,
|
||||
Icon,
|
||||
If,
|
||||
Col,
|
||||
Row,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
ExchangeRateMutedField,
|
||||
} from 'components';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { Features } from 'common';
|
||||
import {
|
||||
inputIntent,
|
||||
momentFormatter,
|
||||
@@ -32,7 +38,12 @@ import {
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -49,7 +60,9 @@ function OwnerDrawingsFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money out dialog context.
|
||||
const { accounts } = useMoneyOutDialogContext();
|
||||
const { accounts, account, branches } = useMoneyOutDialogContext();
|
||||
const { values } = useFormikContext();
|
||||
const isForeigAccount = useForeignAccount();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
@@ -78,8 +91,29 @@ function OwnerDrawingsFormFields({
|
||||
transactionNextNumber,
|
||||
);
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -164,7 +198,7 @@ function OwnerDrawingsFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -180,6 +214,17 @@ function OwnerDrawingsFormFields({
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values?.currency_code}
|
||||
toCurrency={account?.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ equitty account -----------*/}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -18,7 +18,12 @@ import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
InputPrependButton,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
ExchangeRateMutedField,
|
||||
} from 'components';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useAutofocus } from 'hooks';
|
||||
@@ -31,9 +36,15 @@ import {
|
||||
handleDateChange,
|
||||
compose,
|
||||
} from 'utils';
|
||||
import { Features } from 'common';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
|
||||
import { useObserveTransactionNoSettings } from '../utils';
|
||||
import {
|
||||
useObserveTransactionNoSettings,
|
||||
useSetPrimaryBranchToForm,
|
||||
useForeignAccount,
|
||||
BranchRowDivider,
|
||||
} from '../utils';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
@@ -50,7 +61,9 @@ function TransferToAccountFormFields({
|
||||
transactionNextNumber,
|
||||
}) {
|
||||
// Money in dialog context.
|
||||
const { accounts } = useMoneyOutDialogContext();
|
||||
const { accounts, account, branches } = useMoneyOutDialogContext();
|
||||
const { values } = useFormikContext();
|
||||
const isForeigAccount = useForeignAccount();
|
||||
|
||||
const accountRef = useAutofocus();
|
||||
|
||||
@@ -79,8 +92,29 @@ function TransferToAccountFormFields({
|
||||
transactionNextNumber,
|
||||
);
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ Date -----------*/}
|
||||
@@ -165,7 +199,7 @@ function TransferToAccountFormFields({
|
||||
className={'form-group--amount'}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<InputPrependText text={account.currency_code} />
|
||||
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
@@ -180,7 +214,17 @@ function TransferToAccountFormFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={isForeigAccount}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={values?.currency_code}
|
||||
toCurrency={account?.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/*------------ transfer from account -----------*/}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { transactionNumber } from 'utils';
|
||||
import { first, isEqual, isNull } from 'lodash';
|
||||
|
||||
import { useMoneyOutDialogContext } from './MoneyOutDialogProvider';
|
||||
|
||||
export const useObserveTransactionNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
@@ -10,3 +14,33 @@ export const useObserveTransactionNoSettings = (prefix, nextNumber) => {
|
||||
setFieldValue('transacttion_numner', TransactionNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useMoneyOutDialogContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
export const useForeignAccount = () => {
|
||||
const { values } = useFormikContext();
|
||||
const { account } = useMoneyOutDialogContext();
|
||||
|
||||
return (
|
||||
!isEqual(account.currency_code, values.currency_code) &&
|
||||
!isNull(account.currency_code)
|
||||
);
|
||||
};
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
@@ -18,6 +18,8 @@ const Schema = Yup.object().shape({
|
||||
.label(intl.get('payment_account_')),
|
||||
reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
|
||||
// statement: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
||||
branch_id: Yup.string(),
|
||||
exchange_rate: Yup.number(),
|
||||
entries: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
payment_amount: Yup.number().nullable(),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { isEqual } from 'lodash';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
Classes,
|
||||
@@ -16,11 +18,17 @@ import { CLASSES } from 'common/classes';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FieldRequiredHint, Col, Row } from 'components';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { Features } from 'common';
|
||||
import {
|
||||
AccountsSuggestField,
|
||||
InputPrependText,
|
||||
MoneyInputGroup,
|
||||
Icon,
|
||||
If,
|
||||
FeatureCan,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
} from 'components';
|
||||
import {
|
||||
inputIntent,
|
||||
@@ -28,20 +36,49 @@ import {
|
||||
tansformDateValue,
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { useSetPrimaryBranchToForm, useForeignAccount } from './utils';
|
||||
import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Quick payment made form fields.
|
||||
*/
|
||||
export default function QuickPaymentMadeFormFields() {
|
||||
const { accounts } = useQuickPaymentMadeContext();
|
||||
function QuickPaymentMadeFormFields({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
const { accounts, branches, baseCurrency } = useQuickPaymentMadeContext();
|
||||
|
||||
// Intl context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const paymentMadeFieldRef = useAutofocus();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Vendor name ------------- */}
|
||||
@@ -114,6 +151,18 @@ export default function QuickPaymentMadeFormFields() {
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, values.currency_code)}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={values.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.payment_date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Payment date ------------- */}
|
||||
@@ -206,3 +255,11 @@ export default function QuickPaymentMadeFormFields() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withCurrentOrganization())(QuickPaymentMadeFormFields);
|
||||
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { useBill, useAccounts, useCreatePaymentMade } from 'hooks/query';
|
||||
import {
|
||||
useBill,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCreatePaymentMade,
|
||||
} from 'hooks/query';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
|
||||
import { pick } from 'lodash';
|
||||
|
||||
@@ -9,7 +16,11 @@ const QuickPaymentMadeContext = React.createContext();
|
||||
/**
|
||||
* Quick payment made dialog provider.
|
||||
*/
|
||||
function QuickPaymentMadeFormProvider({ billId, dialogName, ...props }) {
|
||||
function QuickPaymentMadeFormProvider({ query, billId, dialogName, ...props }) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch bill details.
|
||||
const { isLoading: isBillLoading, data: bill } = useBill(billId, {
|
||||
enabled: !!billId,
|
||||
@@ -21,6 +32,13 @@ function QuickPaymentMadeFormProvider({ billId, dialogName, ...props }) {
|
||||
// Create payment made mutations.
|
||||
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
bill: {
|
||||
@@ -29,12 +47,16 @@ function QuickPaymentMadeFormProvider({ billId, dialogName, ...props }) {
|
||||
payment_amount: bill?.due_amount,
|
||||
},
|
||||
accounts,
|
||||
branches,
|
||||
dialogName,
|
||||
createPaymentMadeMutate,
|
||||
isBranchesSuccess,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isAccountsLoading || isBillLoading}>
|
||||
<DialogContent
|
||||
isLoading={isAccountsLoading || isBillLoading || isBranchesLoading}
|
||||
>
|
||||
<QuickPaymentMadeContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { first, isEqual } from 'lodash';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider';
|
||||
|
||||
// Default initial values of payment made.
|
||||
export const defaultPaymentMade = {
|
||||
@@ -9,6 +15,8 @@ export const defaultPaymentMade = {
|
||||
reference: '',
|
||||
payment_number: '',
|
||||
// statement: '',
|
||||
exchange_rate: 1,
|
||||
branch_id: '',
|
||||
entries: [{ bill_id: '', payment_amount: '' }],
|
||||
};
|
||||
|
||||
@@ -16,10 +24,7 @@ export const transformErrors = (errors, { setFieldError }) => {
|
||||
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
||||
|
||||
if (getError('PAYMENT.NUMBER.NOT.UNIQUE')) {
|
||||
setFieldError(
|
||||
'payment_number',
|
||||
intl.get('payment_number_is_not_unique'),
|
||||
);
|
||||
setFieldError('payment_number', intl.get('payment_number_is_not_unique'));
|
||||
}
|
||||
if (getError('INVALID_BILL_PAYMENT_AMOUNT')) {
|
||||
setFieldError(
|
||||
@@ -27,4 +32,25 @@ export const transformErrors = (errors, { setFieldError }) => {
|
||||
intl.get('the_payment_amount_bigger_than_invoice_due_amount'),
|
||||
);
|
||||
}
|
||||
if (getError('WITHDRAWAL_ACCOUNT_CURRENCY_INVALID')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('payment_made.error.withdrawal_account_currency_invalid'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useQuickPaymentMadeContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
@@ -3,22 +3,20 @@ import intl from 'react-intl-universal';
|
||||
import { DATATYPES_LENGTH } from 'common/dataTypes';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
customer_id: Yup.string()
|
||||
.label(intl.get('customer_name_'))
|
||||
.required(),
|
||||
customer_id: Yup.string().label(intl.get('customer_name_')).required(),
|
||||
payment_receive_no: Yup.string()
|
||||
.required()
|
||||
.nullable()
|
||||
.max(DATATYPES_LENGTH.STRING)
|
||||
.label(intl.get('payment_receive_no_')),
|
||||
payment_date: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('payment_date_')),
|
||||
payment_date: Yup.date().required().label(intl.get('payment_date_')),
|
||||
deposit_account_id: Yup.number()
|
||||
.required()
|
||||
.label(intl.get('deposit_account_')),
|
||||
reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
|
||||
// statement: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
||||
branch_id: Yup.string(),
|
||||
exchange_rate: Yup.number(),
|
||||
entries: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
payment_amount: Yup.number().nullable(),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -20,32 +22,66 @@ import {
|
||||
InputPrependText,
|
||||
MoneyInputGroup,
|
||||
Icon,
|
||||
If,
|
||||
FeatureCan,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
} from 'components';
|
||||
import { Features } from 'common';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import {
|
||||
inputIntent,
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
handleDateChange,
|
||||
compose
|
||||
compose,
|
||||
} from 'utils';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
import { useQuickPaymentReceiveContext } from './QuickPaymentReceiveFormProvider';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
/**
|
||||
* Quick payment receive form fields.
|
||||
*/
|
||||
function QuickPaymentReceiveFormFields({
|
||||
paymentReceiveAutoIncrement
|
||||
paymentReceiveAutoIncrement,
|
||||
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
const { accounts } = useQuickPaymentReceiveContext();
|
||||
const { accounts, branches, baseCurrency } = useQuickPaymentReceiveContext();
|
||||
|
||||
// Intl context.
|
||||
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const paymentReceiveFieldRef = useAutofocus();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Customer name ------------- */}
|
||||
@@ -120,6 +156,19 @@ function QuickPaymentReceiveFormFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, values.currency_code)}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={values.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.payment_date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Payment date ------------- */}
|
||||
@@ -199,6 +248,7 @@ function QuickPaymentReceiveFormFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'statement'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
@@ -218,4 +268,11 @@ export default compose(
|
||||
withSettings(({ paymentReceiveSettings }) => ({
|
||||
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
|
||||
})),
|
||||
)(QuickPaymentReceiveFormFields)
|
||||
withCurrentOrganization(),
|
||||
)(QuickPaymentReceiveFormFields);
|
||||
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
@@ -1,14 +1,31 @@
|
||||
import React, { useContext, createContext } from 'react';
|
||||
import { pick } from 'lodash';
|
||||
import { DialogContent } from 'components';
|
||||
import { useAccounts, useInvoice, useSettingsPaymentReceives, useCreatePaymentReceive } from 'hooks/query';
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useInvoice,
|
||||
useBranches,
|
||||
useSettingsPaymentReceives,
|
||||
useCreatePaymentReceive,
|
||||
} from 'hooks/query';
|
||||
|
||||
const QuickPaymentReceiveContext = createContext();
|
||||
|
||||
/**
|
||||
* Quick payment receive dialog provider.
|
||||
*/
|
||||
function QuickPaymentReceiveFormProvider({ invoiceId, dialogName, ...props }) {
|
||||
function QuickPaymentReceiveFormProvider({
|
||||
query,
|
||||
invoiceId,
|
||||
dialogName,
|
||||
baseCurrency,
|
||||
...props
|
||||
}) {
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
@@ -22,9 +39,17 @@ function QuickPaymentReceiveFormProvider({ invoiceId, dialogName, ...props }) {
|
||||
// Fetch payment made settings.
|
||||
const { isLoading: isSettingsLoading } = useSettingsPaymentReceives();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// State provider.
|
||||
const provider = {
|
||||
accounts,
|
||||
branches,
|
||||
invoice: {
|
||||
...pick(invoice, ['id', 'due_amount', 'customer', 'currency_code']),
|
||||
customer_id: invoice?.customer?.display_name,
|
||||
@@ -32,13 +57,16 @@ function QuickPaymentReceiveFormProvider({ invoiceId, dialogName, ...props }) {
|
||||
},
|
||||
isAccountsLoading,
|
||||
isSettingsLoading,
|
||||
isBranchesSuccess,
|
||||
dialogName,
|
||||
|
||||
baseCurrency,
|
||||
createPaymentReceiveMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isAccountsLoading || isInvoiceLoading}>
|
||||
<DialogContent
|
||||
isLoading={isAccountsLoading || isInvoiceLoading || isBranchesLoading}
|
||||
>
|
||||
<QuickPaymentReceiveContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { first } from 'lodash';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useQuickPaymentReceiveContext } from './QuickPaymentReceiveFormProvider';
|
||||
|
||||
export const defaultInitialValues = {
|
||||
customer_id: '',
|
||||
@@ -8,6 +15,8 @@ export const defaultInitialValues = {
|
||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
reference_no: '',
|
||||
// statement: '',
|
||||
exchange_rate: 1,
|
||||
branch_id: '',
|
||||
entries: [{ invoice_id: '', payment_amount: '' }],
|
||||
};
|
||||
|
||||
@@ -32,4 +41,25 @@ export const transformErrors = (errors, { setFieldError }) => {
|
||||
intl.get('the_payment_amount_bigger_than_invoice_due_amount'),
|
||||
);
|
||||
}
|
||||
if (getError('PAYMENT_ACCOUNT_CURRENCY_INVALID')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('payment_Receive.error.payment_account_currency_invalid'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useQuickPaymentReceiveContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ const defaultInitialValues = {
|
||||
reference_no: '',
|
||||
description: '',
|
||||
amount: '',
|
||||
exchange_rate: 1,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,5 +8,6 @@ const Schema = Yup.object().shape({
|
||||
reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
|
||||
from_account_id: Yup.number().required().label(intl.get('deposit_account_')),
|
||||
description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
||||
exchange_rate: Yup.number(),
|
||||
});
|
||||
export const CreateRefundCreditNoteFormSchema = Schema;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { isEqual } from 'lodash';
|
||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -11,14 +13,22 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Features } from 'common';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
FieldRequiredHint,
|
||||
AccountsSuggestField,
|
||||
InputPrependText,
|
||||
MoneyInputGroup,
|
||||
FormattedMessage as T,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
} from 'components';
|
||||
import {
|
||||
inputIntent,
|
||||
@@ -28,41 +38,112 @@ import {
|
||||
} from 'utils';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Refund credit note form fields.
|
||||
*/
|
||||
function RefundCreditNoteFormFields() {
|
||||
const { accounts } = useRefundCreditNoteContext();
|
||||
function RefundCreditNoteFormFields({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
const { accounts, branches } = useRefundCreditNoteContext();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="date" />}
|
||||
// inline={true}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="date" />}
|
||||
// inline={true}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
<Col xs={5}>
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'from_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.from_account'} />}
|
||||
className={classNames(
|
||||
'form-group--from_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'from_account_id'} />}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('from_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
{/* ------------- Amount ------------- */}
|
||||
<FastField name={'amount'}>
|
||||
{({
|
||||
@@ -76,7 +157,6 @@ function RefundCreditNoteFormFields() {
|
||||
className={classNames('form-group--amount', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
// inline={true}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
@@ -93,6 +173,19 @@ function RefundCreditNoteFormFields() {
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, values.currency_code)}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={values.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
@@ -101,7 +194,6 @@ function RefundCreditNoteFormFields() {
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
// inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
@@ -112,46 +204,12 @@ function RefundCreditNoteFormFields() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'from_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.from_account'} />}
|
||||
className={classNames(
|
||||
'form-group--from_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'from_account_id'} />}
|
||||
// inline={true}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('from_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'description'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.description'} />}
|
||||
className={'form-group--description'}
|
||||
// inline={true}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
@@ -161,4 +219,10 @@ function RefundCreditNoteFormFields() {
|
||||
);
|
||||
}
|
||||
|
||||
export default RefundCreditNoteFormFields;
|
||||
export default compose(withCurrentOrganization())(RefundCreditNoteFormFields);
|
||||
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 13px;
|
||||
`;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useCreditNote,
|
||||
useBranches,
|
||||
useCreateRefundCreditNote,
|
||||
} from 'hooks/query';
|
||||
|
||||
@@ -13,7 +15,16 @@ const RefundCreditNoteContext = React.createContext();
|
||||
/**
|
||||
* Refund credit note form provider.
|
||||
*/
|
||||
function RefundCreditNoteFormProvider({ creditNoteId, dialogName, ...props }) {
|
||||
function RefundCreditNoteFormProvider({
|
||||
creditNoteId,
|
||||
dialogName,
|
||||
query,
|
||||
...props
|
||||
}) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
@@ -24,6 +35,14 @@ function RefundCreditNoteFormProvider({ creditNoteId, dialogName, ...props }) {
|
||||
enabled: !!creditNoteId,
|
||||
},
|
||||
);
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Create and edit credit note mutations.
|
||||
const { mutateAsync: createRefundCreditNoteMutate } =
|
||||
useCreateRefundCreditNote();
|
||||
@@ -35,12 +54,17 @@ function RefundCreditNoteFormProvider({ creditNoteId, dialogName, ...props }) {
|
||||
amount: creditNote.credits_remaining,
|
||||
},
|
||||
accounts,
|
||||
branches,
|
||||
dialogName,
|
||||
isBranchesSuccess,
|
||||
|
||||
createRefundCreditNoteMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isAccountsLoading || isCreditNoteLoading}>
|
||||
<DialogContent
|
||||
isLoading={isAccountsLoading || isCreditNoteLoading || isBranchesLoading}
|
||||
>
|
||||
<RefundCreditNoteContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { first } from 'lodash';
|
||||
|
||||
import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider';
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useRefundCreditNoteContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
@@ -3,14 +3,13 @@ import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import moment from 'moment';
|
||||
import { omit, defaultTo } from 'lodash';
|
||||
import { omit } from 'lodash';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
|
||||
import { CreateVendorRefundCreditFormSchema } from './RefundVendorCreditForm.schema';
|
||||
import RefundVendorCreditFormContent from './RefundVendorCreditFormContent';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose, transactionNumber } from 'utils';
|
||||
|
||||
@@ -20,6 +19,7 @@ const defaultInitialValues = {
|
||||
reference_no: '',
|
||||
description: '',
|
||||
amount: '',
|
||||
exchange_rate: 1,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,5 +10,6 @@ const Schema = Yup.object().shape({
|
||||
.required()
|
||||
.label(intl.get('deposit_account_')),
|
||||
description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
||||
exchange_rate: Yup.number(),
|
||||
});
|
||||
export const CreateVendorRefundCreditFormSchema = Schema;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -12,13 +13,21 @@ import {
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
If,
|
||||
FieldRequiredHint,
|
||||
AccountsSuggestField,
|
||||
InputPrependText,
|
||||
MoneyInputGroup,
|
||||
FormattedMessage as T,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
} from 'components';
|
||||
import {
|
||||
inputIntent,
|
||||
@@ -28,42 +37,113 @@ import {
|
||||
compose,
|
||||
} from 'utils';
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { Features } from 'common';
|
||||
import { ACCOUNT_TYPE } from 'common/accountTypes';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
/**
|
||||
* Refund Vendor credit form fields.
|
||||
*/
|
||||
function RefundVendorCreditFormFields() {
|
||||
const { accounts } = useRefundVendorCreditContext();
|
||||
function RefundVendorCreditFormFields({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
const { accounts, branches } = useRefundVendorCreditContext();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const amountFieldRef = useAutofocus();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'refund_date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="refund_date" />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('refund_date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<BranchRowDivider />
|
||||
</FeatureCan>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'refund_date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="refund_date" />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('refund_date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col xs={5}>
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<T id={'refund_vendor_credit.dialog.deposit_to_account'} />
|
||||
}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('deposit_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* ------------- Amount ------------- */}
|
||||
<FastField name={'amount'}>
|
||||
{({
|
||||
@@ -93,6 +173,19 @@ function RefundVendorCreditFormFields() {
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, values.currency_code)}>
|
||||
{/*------------ exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={values.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.date}
|
||||
exchangeRate={values.exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
@@ -111,38 +204,6 @@ function RefundVendorCreditFormFields() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.deposit_to_account'} />}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('deposit_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'description'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
@@ -158,4 +219,10 @@ function RefundVendorCreditFormFields() {
|
||||
);
|
||||
}
|
||||
|
||||
export default RefundVendorCreditFormFields;
|
||||
export default compose(withCurrentOrganization())(RefundVendorCreditFormFields);
|
||||
|
||||
export const BranchRowDivider = styled.div`
|
||||
height: 1px;
|
||||
background: #ebf1f6;
|
||||
margin-bottom: 13px;
|
||||
`;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { Features } from 'common';
|
||||
import { useFeatureCan } from 'hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useVendorCredit,
|
||||
useBranches,
|
||||
useCreateRefundVendorCredit,
|
||||
} from 'hooks/query';
|
||||
|
||||
@@ -13,11 +15,23 @@ const RefundVendorCreditContext = React.createContext();
|
||||
function RefundVendorCreditFormProvider({
|
||||
vendorCreditId,
|
||||
dialogName,
|
||||
query,
|
||||
...props
|
||||
}) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isBranchesLoading,
|
||||
isSuccess: isBranchesSuccess,
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Handle fetch vendor credit details.
|
||||
const { data: vendorCredit, isLoading: isVendorCreditLoading } =
|
||||
useVendorCredit(vendorCreditId, {
|
||||
@@ -35,12 +49,18 @@ function RefundVendorCreditFormProvider({
|
||||
amount: vendorCredit.credits_remaining,
|
||||
},
|
||||
accounts,
|
||||
branches,
|
||||
dialogName,
|
||||
isBranchesSuccess,
|
||||
createRefundVendorCreditMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isAccountsLoading || isVendorCreditLoading}>
|
||||
<DialogContent
|
||||
isLoading={
|
||||
isAccountsLoading || isVendorCreditLoading || isBranchesLoading
|
||||
}
|
||||
>
|
||||
<RefundVendorCreditContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
20
src/containers/Dialogs/RefundVendorCreditDialog/utils.js
Normal file
20
src/containers/Dialogs/RefundVendorCreditDialog/utils.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { first } from 'lodash';
|
||||
|
||||
import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
|
||||
|
||||
export const useSetPrimaryBranchToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { branches, isBranchesSuccess } = useRefundVendorCreditContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isBranchesSuccess) {
|
||||
const primaryBranch = branches.find((b) => b.primary) || first(branches);
|
||||
|
||||
if (primaryBranch) {
|
||||
setFieldValue('branch_id', primaryBranch.id);
|
||||
}
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/pages/VendorOpeningBalance/VendorOpeningBalance.scss';
|
||||
|
||||
import VendorOpeningBalanceForm from './VendorOpeningBalanceForm';
|
||||
import { VendorOpeningBalanceFormProvider } from './VendorOpeningBalanceFormProvider';
|
||||
|
||||
/**
|
||||
* Vendor Opening balance dialog content.
|
||||
* @returns
|
||||
*/
|
||||
export default function VendorOpeningBalanceDialogContent({
|
||||
// #ownProps
|
||||
dialogName,
|
||||
vendorId,
|
||||
}) {
|
||||
return (
|
||||
<VendorOpeningBalanceFormProvider
|
||||
vendorId={vendorId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<VendorOpeningBalanceForm />
|
||||
</VendorOpeningBalanceFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Formik } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { CreateVendorOpeningBalanceFormSchema } from './VendorOpeningBalanceForm.schema';
|
||||
import { useVendorOpeningBalanceContext } from './VendorOpeningBalanceFormProvider';
|
||||
|
||||
import VendorOpeningBalanceFormContent from './VendorOpeningBalanceFormContent';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const defaultInitialValues = {
|
||||
opening_balance: '0',
|
||||
opening_balance_branch_id: '',
|
||||
opening_balance_exchange_rate: 1,
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
};
|
||||
|
||||
/**
|
||||
* Vendor Opening balance form.
|
||||
* @returns
|
||||
*/
|
||||
function VendorOpeningBalanceForm({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { dialogName, vendor, editVendorOpeningBalanceMutate } =
|
||||
useVendorOpeningBalanceContext();
|
||||
|
||||
// Initial form values
|
||||
const initialValues = {
|
||||
...defaultInitialValues,
|
||||
...vendor,
|
||||
opening_balance: defaultTo(vendor.opening_balance, ''),
|
||||
|
||||
};
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const formValues = {
|
||||
...values,
|
||||
};
|
||||
|
||||
// Handle request response success.
|
||||
const onSuccess = (response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('vendor_opening_balance.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDialog(dialogName);
|
||||
};
|
||||
|
||||
// Handle request response errors.
|
||||
const onError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors) {
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
editVendorOpeningBalanceMutate([vendor.id, formValues])
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={CreateVendorOpeningBalanceFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={VendorOpeningBalanceFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(VendorOpeningBalanceForm);
|
||||
@@ -0,0 +1,10 @@
|
||||
import * as Yup from 'yup';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
opening_balance_branch_id: Yup.string(),
|
||||
opening_balance: Yup.number().nullable(),
|
||||
opening_balance_at: Yup.date(),
|
||||
opening_balance_exchange_rate: Yup.number(),
|
||||
});
|
||||
|
||||
export const CreateVendorOpeningBalanceFormSchema = Schema;
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Form } from 'formik';
|
||||
|
||||
import VendorOpeningBalanceFormFields from './VendorOpeningBalanceFormFields';
|
||||
import VendorOpeningBalanceFormFloatingActions from './VendorOpeningBalanceFormFloatingActions';
|
||||
|
||||
/**
|
||||
* Vendor Opening balance form content.
|
||||
* @returns
|
||||
*/
|
||||
function VendorOpeningBalanceFormContent() {
|
||||
return (
|
||||
<Form>
|
||||
<VendorOpeningBalanceFormFields />
|
||||
<VendorOpeningBalanceFormFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default VendorOpeningBalanceFormContent;
|
||||
@@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { Classes, Position, FormGroup, ControlGroup } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { isEqual } from 'lodash';
|
||||
import { FastField, useFormikContext } from 'formik';
|
||||
import { momentFormatter, tansformDateValue, handleDateChange } from 'utils';
|
||||
import { Features } from 'common';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
If,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
ExchangeRateMutedField,
|
||||
BranchSelect,
|
||||
BranchSelectButton,
|
||||
FeatureCan,
|
||||
InputPrependText,
|
||||
} from 'components';
|
||||
import { FMoneyInputGroup, FFormGroup } from '../../../components/Forms';
|
||||
|
||||
import { useVendorOpeningBalanceContext } from './VendorOpeningBalanceFormProvider';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Vendor Opening balance form fields.
|
||||
* @returns
|
||||
*/
|
||||
function VendorOpeningBalanceFormFields({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
// Formik context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const { branches, vendor } = useVendorOpeningBalanceContext();
|
||||
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------ Opening balance -----------*/}
|
||||
<FFormGroup
|
||||
name={'opening_balance'}
|
||||
label={<T id={'vendor_opening_balance.label.opening_balance'} />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={vendor.currency_code} />
|
||||
<FMoneyInputGroup
|
||||
name={'opening_balance'}
|
||||
allowDecimals={true}
|
||||
allowNegativeValue={true}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Opening balance at -----------*/}
|
||||
<FastField name={'opening_balance_at'}>
|
||||
{({ form, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'vendor_opening_balance.label.opening_balance_at'} />}
|
||||
className={Classes.FILL}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('opening_balance_at', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={!isEqual(base_currency, vendor.currency_code)}>
|
||||
{/*------------ Opening balance exchange rate -----------*/}
|
||||
<ExchangeRateMutedField
|
||||
name={'opening_balance_exchange_rate'}
|
||||
fromCurrency={base_currency}
|
||||
toCurrency={vendor.currency_code}
|
||||
formGroupProps={{ label: '', inline: false }}
|
||||
date={values.opening_balance_at}
|
||||
exchangeRate={values.opening_balance_exchange_rate}
|
||||
/>
|
||||
</If>
|
||||
|
||||
{/*------------ Opening balance branch id -----------*/}
|
||||
<FeatureCan feature={Features.Branches}>
|
||||
<FFormGroup
|
||||
label={<T id={'branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</FeatureCan>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withCurrentOrganization())(
|
||||
VendorOpeningBalanceFormFields,
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user