Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ahmed Bouhuolia
2020-11-24 12:48:27 +02:00
25 changed files with 192 additions and 117 deletions

View File

@@ -38,7 +38,7 @@ export default function MakeJournalEntriesFooter({
disabled={isSubmitting}
className={'button-secondary ml1'}
onClick={() => {
onSubmitClick({ publish: false, redirect: false });
onSubmitClick({ publish: false, redirect: true });
}}
>
<T id={'save_as_draft'} />

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { Route, Switch, useHistory, withRouter } from 'react-router-dom';
import { useQuery } from 'react-query';
import { queryCache, useQuery } from 'react-query';
import { Alert, Intent } from '@blueprintjs/core';
import AppToaster from 'components/AppToaster';
import {
@@ -53,6 +53,7 @@ function ManualJournalsTable({
const [deleteManualJournal, setDeleteManualJournal] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [bulkDelete, setBulkDelete] = useState(false);
const [publishManualJournal, setPublishManualJournal] = useState(false);
const { formatMessage } = useIntl();
@@ -176,19 +177,36 @@ function ManualJournalsTable({
[addManualJournalsTableQueries],
);
const handlePublishJournal = useCallback(
(journal) => {
requestPublishManualJournal(journal.id).then(() => {
// Handle publish manual Journal click.
const handlePublishMaunalJournal = useCallback(
(jouranl) => {
setPublishManualJournal(jouranl);
},
[setPublishManualJournal],
);
// Handle cancel manual journal alert.
const handleCancelPublishMaunalJournal = useCallback(() => {
setPublishManualJournal(false);
}, [setPublishManualJournal]);
// Handle publish manual journal confirm.
const handleConfirmPublishManualJournal = useCallback(() => {
requestPublishManualJournal(publishManualJournal.id)
.then(() => {
setPublishManualJournal(false);
AppToaster.show({
message: formatMessage({
id: 'the_manual_journal_id_has_been_published',
id: 'the_manual_journal_has_been_published',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('manual-journals-table');
})
.catch((error) => {
setPublishManualJournal(false);
});
},
[requestPublishManualJournal, formatMessage],
);
}, [publishManualJournal, requestPublishManualJournal, formatMessage]);
// Handle selected rows change.
const handleSelectedRowsChange = useCallback(
@@ -223,7 +241,7 @@ function ManualJournalsTable({
<ManualJournalsDataTable
onDeleteJournal={handleDeleteJournal}
onEditJournal={handleEditJournal}
onPublishJournal={handlePublishJournal}
onPublishJournal={handlePublishMaunalJournal}
onSelectedRowsChange={handleSelectedRowsChange}
/>
</Route>
@@ -260,6 +278,18 @@ function ManualJournalsTable({
/>
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'publish'} />}
intent={Intent.WARNING}
isOpen={publishManualJournal}
onCancel={handleCancelPublishMaunalJournal}
onConfirm={handleConfirmPublishManualJournal}
>
<p>
<T id={'are_sure_to_publish_this_manual_journal'} />
</p>
</Alert>
</DashboardPageContent>
</DashboardInsider>
);

View File

@@ -159,7 +159,7 @@ const CustomerBillingAddress = ({}) => {
inline={true}
helperText={<ErrorMessage name="shipping_address_2" />}
>
<InputGroup {...field} />
<TextArea {...field} />
</FormGroup>
)}
</FastField>

View File

@@ -1,9 +1,15 @@
import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import { FormGroup, Position, Classes } from '@blueprintjs/core';
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { MoneyInputGroup, CurrencySelectList, Row, Col } from 'components';
import {
MoneyInputGroup,
InputPrependText,
CurrencySelectList,
Row,
Col,
} from 'components';
import { FormattedMessage as T } from 'react-intl';
import withCurrencies from 'containers/Currencies/withCurrencies';
@@ -21,20 +27,7 @@ function CustomerFinancialPanel({
customerId,
}) {
const [selectedItems, setSelectedItems] = useState();
const onItemsSelect = useCallback(
(filedName) => {
return (filed) => {
setSelectedItems({
...selectedItems,
[filedName]: filed,
});
// setFieldValue(filedName, filed.currency_code);
};
},
[selectedItems],
);
return (
<div className={'tab-panel--financial'}>
<Row>
@@ -52,7 +45,6 @@ function CustomerFinancialPanel({
<DateInput
{...momentFormatter('YYYY/MM/DD')}
value={tansformDateValue(value)}
// onChange={}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
disabled={customerId}
/>
@@ -62,7 +54,12 @@ function CustomerFinancialPanel({
{/*------------ Opening balance -----------*/}
<FastField name={'opening_balance'}>
{({ field, field: { value }, meta: { error, touched } }) => (
{({
form: { values },
field,
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'opening_balance'} />}
className={classNames(
@@ -72,13 +69,15 @@ function CustomerFinancialPanel({
intent={inputIntent({ error, touched })}
inline={true}
>
<MoneyInputGroup
value={value}
prefix={'$'}
inputGroupProps={{ fill: true }}
disabled={customerId}
{...field}
/>
<ControlGroup>
<InputPrependText text={values.currency_code} />
<MoneyInputGroup
value={value}
inputGroupProps={{ fill: true }}
disabled={customerId}
{...field}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
@@ -98,7 +97,9 @@ function CustomerFinancialPanel({
<CurrencySelectList
currenciesList={currenciesList}
selectedCurrencyCode={value}
onCurrencySelected={onItemsSelect('currency_code')}
onCurrencySelected={(currency) => {
form.setFieldValue('currency_code', currency.currency_code);
}}
disabled={customerId}
/>
</FormGroup>

View File

@@ -20,6 +20,7 @@ import withCustomerDetail from 'containers/Customers/withCustomerDetail';
import withCustomersActions from 'containers/Customers/withCustomersActions';
import withMediaActions from 'containers/Media/withMediaActions';
import withCustomers from 'containers/Customers//withCustomers';
import withSettings from 'containers/Settings/withSettings';
import useMedia from 'hooks/useMedia';
import { compose, transformToForm } from 'utils';
@@ -73,6 +74,9 @@ function CustomerForm({
// #withCustomerDetail
customer,
// #withSettings
baseCurrency,
// #withCustomersActions
requestSubmitCustomer,
requestEditCustomer,
@@ -151,6 +155,7 @@ function CustomerForm({
const initialValues = useMemo(
() => ({
...defaultInitialValues,
currency_code: baseCurrency,
...transformToForm(customer, defaultInitialValues),
}),
[customer, defaultInitialValues],
@@ -250,7 +255,7 @@ function CustomerForm({
</div>
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
<CustomersTabs customerId={customerId} />
<CustomersTabs customer={customerId} />
</div>
<CustomerFloatingActions
@@ -271,6 +276,9 @@ export default compose(
withCustomers(({ customers }) => ({
customers,
})),
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withDashboardActions,
withCustomersActions,
withMediaActions,

View File

@@ -6,16 +6,9 @@ import CustomerAttachmentTabs from './CustomerAttachmentTabs';
import CustomerFinancialPanel from './CustomerFinancialPanel';
import CustomerNotePanel from './CustomerNotePanel';
export default function CustomersTabs({
setFieldValue,
getFieldProps,
errors,
values,
touched,
customerId,
}) {
export default function CustomersTabs({ customer }) {
const { formatMessage } = useIntl();
return (
<div>
<Tabs
@@ -27,7 +20,7 @@ export default function CustomersTabs({
<Tab
id={'financial'}
title={formatMessage({ id: 'financial_details' })}
panel={<CustomerFinancialPanel />}
panel={<CustomerFinancialPanel customerId={customer} />}
/>
<Tab
id={'address'}

View File

@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { getCustomerById } from 'store/customers/customers.reducer';
const mapStateToProps = (state, props) => ({
customerDetail: getCustomerById(state, props.customerId),
customer: getCustomerById(state, props.customerId),
});
export default connect(mapStateToProps);

View File

@@ -19,6 +19,7 @@ const Schema = Yup.object().shape({
.trim()
.min(1)
.max(DATATYPES_LENGTH.TEXT)
.nullable()
.label(formatMessage({ id: 'description' })),
publish: Yup.boolean().label(formatMessage({ id: 'publish' })),
categories: Yup.array().of(

View File

@@ -158,7 +158,7 @@ function BillsDataTable({
{
id: 'bill_number',
Header: formatMessage({ id: 'bill_number' }),
accessor: (row) => `#${row.bill_number}`,
accessor: (row) => (row.bill_number ? `#${row.bill_number}` : null),
width: 140,
className: 'bill_number',
},

View File

@@ -110,7 +110,7 @@ function PaymentMadeDataTable({
{
id: 'payment_number',
Header: formatMessage({ id: 'payment_number' }),
accessor: (row) => `#${row.payment_number}`,
accessor: (row) => (row.payment_number ? `#${row.payment_number}` : null),
width: 140,
className: 'payment_number',
},

View File

@@ -15,6 +15,7 @@ const Schema = Yup.object().shape({
payment_number: Yup.string()
.nullable()
.max(DATATYPES_LENGTH.STRING)
.nullable()
.label(formatMessage({ id: 'payment_no_' })),
reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
description: Yup.string().max(DATATYPES_LENGTH.TEXT),

View File

@@ -163,7 +163,6 @@ function PaymentMadeFormHeader({
label={<T id={'payment_no'} />}
inline={true}
className={('form-group--payment_number', Classes.FILL)}
labelInfo={<FieldRequiredHint />}
intent={
errors.payment_number && touched.payment_number && Intent.DANGER
}

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { Button } from '@blueprintjs/core';
import { Button } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
import { sumBy } from 'lodash';
@@ -26,7 +26,7 @@ const CellRenderer = (content, type) => (props) => {
const TotalCellRederer = (content, type) => (props) => {
if (props.data.length === props.row.index + 1) {
return <Money amount={props.cell.row.original[type]} currency={'USD'} />
return <Money amount={props.cell.row.original[type]} currency={'USD'} />;
}
return content(props);
};
@@ -40,15 +40,17 @@ export default function PaymentMadeItemsTableEditor({
onUpdateData,
data,
errors,
noResultsMessage
noResultsMessage,
}) {
const transformedData = useMemo(() => {
const rows = [ ...data ];
const rows = [...data];
const totalRow = {
due_amount: sumBy(data, 'due_amount'),
payment_amount: sumBy(data, 'payment_amount'),
};
if (rows.length > 0) { rows.push(totalRow) }
if (rows.length > 0) {
rows.push(totalRow);
}
return rows;
}, [data]);
@@ -80,7 +82,7 @@ export default function PaymentMadeItemsTableEditor({
},
{
Header: formatMessage({ id: 'bill_number' }),
accessor: (row) => `#${row?.bill_number}`,
accessor: (row) => `#${row?.bill_number || ''}`,
Cell: CellRenderer(EmptyDiv, 'bill_number'),
disableSortBy: true,
className: 'bill_number',
@@ -116,7 +118,7 @@ export default function PaymentMadeItemsTableEditor({
};
const rowClassNames = useCallback(
(row) => ({ 'row--total': localData.length === row.index + 1 }),
(row) => ({ 'row--total': localData.length === row.index + 1 }),
[localData],
);
@@ -129,7 +131,7 @@ export default function PaymentMadeItemsTableEditor({
columnId,
value,
);
newRows.splice(-1,1); // removes the total row.
newRows.splice(-1, 1); // removes the total row.
setLocalData(newRows);
onUpdateData && onUpdateData(newRows);
@@ -138,10 +140,12 @@ export default function PaymentMadeItemsTableEditor({
);
return (
<div className={classNames(
CLASSES.DATATABLE_EDITOR,
CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES,
)}>
<div
className={classNames(
CLASSES.DATATABLE_EDITOR,
CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES,
)}
>
<DataTable
columns={columns}
data={localData}

View File

@@ -42,10 +42,10 @@ const MIN_LINES_NUMBER = 4;
const defaultEstimate = {
index: 0,
item_id: null,
rate: null,
item_id: '',
rate: '',
discount: 0,
quantity: null,
quantity: '',
description: '',
};

View File

@@ -114,7 +114,7 @@ function EstimatesDataTable({
{
id: 'estimate_number',
Header: formatMessage({ id: 'estimate_number' }),
accessor: (row) => `#${row.estimate_number}`,
accessor: (row) => (row.estimate_number ? `#${row.estimate_number}` : null),
width: 140,
className: 'estimate_number',
},

View File

@@ -42,10 +42,10 @@ const MIN_LINES_NUMBER = 4;
const defaultInvoice = {
index: 0,
item_id: null,
rate: null,
item_id: '',
rate: '',
discount: 0,
quantity: null,
quantity: '',
description: '',
};

View File

@@ -112,7 +112,7 @@ function InvoicesDataTable({
{
id: 'invoice_no',
Header: formatMessage({ id: 'invoice_no__' }),
accessor: (row) => `#${row.invoice_no}`,
accessor: (row) => (row.invoice_no ? `#${row.invoice_no}` : null),
width: 140,
className: 'invoice_no',
},

View File

@@ -109,10 +109,10 @@ function PaymentReceiveForm({
// Default payment receive entry.
const defaultPaymentReceiveEntry = {
id: null,
payment_amount: null,
invoice_id: null,
due_amount: null,
id: '',
payment_amount: '',
invoice_id: '',
due_amount: '',
};
// Form initial values.

View File

@@ -21,12 +21,15 @@ import {
FieldRequiredHint,
Icon,
InputPrependButton,
MoneyInputGroup,
InputPrependText,
Hint,
Money,
} from 'components';
import withCustomers from 'containers/Customers/withCustomers';
import withAccounts from 'containers/Accounts/withAccounts';
import withSettings from 'containers/Settings/withSettings';
import withDialogActions from 'containers/Dialog/withDialogActions';
function PaymentReceiveFormHeader({
@@ -47,6 +50,9 @@ function PaymentReceiveFormHeader({
//#withAccouts
accountsList,
//#withSettings
baseCurrency,
// #withInvoices
receivableInvoices,
// #ownProps
@@ -150,15 +156,17 @@ function PaymentReceiveFormHeader({
<ErrorMessage name="full_amount" {...{ errors, touched }} />
}
>
<InputGroup
intent={
errors.full_amount && touched.full_amount && Intent.DANGER
}
minimal={true}
value={values.full_amount}
{...getFieldProps('full_amount')}
onBlur={handleFullAmountBlur}
/>
<ControlGroup>
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={values.full_amount}
inputGroupProps={{
medium: true,
onBlur: { handleFullAmountBlur },
...getFieldProps('full_amount'),
}}
/>
</ControlGroup>
<a
onClick={handleReceiveFullAmountClick}
@@ -166,7 +174,7 @@ function PaymentReceiveFormHeader({
className={'receive-full-amount'}
>
Receive full amount (
<Money amount={receivableFullAmount} currency={'USD'} />)
<Money amount={receivableFullAmount} currency={baseCurrency} />)
</a>
</FormGroup>
@@ -270,7 +278,7 @@ function PaymentReceiveFormHeader({
<div class="big-amount">
<span class="big-amount__label">Amount Received</span>
<h1 class="big-amount__number">
<Money amount={amountReceived} currency={'USD'} />
<Money amount={amountReceived} currency={baseCurrency} />
</h1>
</div>
</div>
@@ -286,5 +294,8 @@ export default compose(
withAccounts(({ accountsList }) => ({
accountsList,
})),
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withDialogActions,
)(PaymentReceiveFormHeader);

View File

@@ -26,12 +26,12 @@ const CellRenderer = (content, type) => (props) => {
const TotalCellRederer = (content, type) => (props) => {
if (props.data.length === props.row.index + 1) {
return <Money amount={props.cell.row.original[type]} currency={'USD'} />
return <Money amount={props.cell.row.original[type]} currency={'USD'} />;
}
return content(props);
};
export default function PaymentReceiveItemsTableEditor ({
export default function PaymentReceiveItemsTableEditor({
onClickClearAllLines,
onUpdateData,
data,
@@ -39,12 +39,14 @@ export default function PaymentReceiveItemsTableEditor ({
noResultsMessage,
}) {
const transformedData = useMemo(() => {
const rows = [ ...data ];
const rows = [...data];
const totalRow = {
due_amount: sumBy(data, 'due_amount'),
payment_amount: sumBy(data, 'payment_amount'),
};
if (rows.length > 0) { rows.push(totalRow) }
if (rows.length > 0) {
rows.push(totalRow);
}
return rows;
}, [data]);
@@ -81,7 +83,7 @@ export default function PaymentReceiveItemsTableEditor ({
Header: formatMessage({ id: 'invocie_number' }),
accessor: (row) => {
const invNumber = row?.invoice_no || row?.id;
return `#INV-${invNumber}`;
return `#INV-${invNumber || ''}`;
},
Cell: CellRenderer(EmptyDiv, 'invoice_no'),
disableSortBy: true,
@@ -121,7 +123,7 @@ export default function PaymentReceiveItemsTableEditor ({
};
const rowClassNames = useCallback(
(row) => ({ 'row--total': localData.length === row.index + 1 }),
(row) => ({ 'row--total': localData.length === row.index + 1 }),
[localData],
);
@@ -144,10 +146,12 @@ export default function PaymentReceiveItemsTableEditor ({
);
return (
<div className={classNames(
CLASSES.DATATABLE_EDITOR,
CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES,
)}>
<div
className={classNames(
CLASSES.DATATABLE_EDITOR,
CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES,
)}
>
<DataTable
columns={columns}
data={localData}
@@ -170,5 +174,4 @@ export default function PaymentReceiveItemsTableEditor ({
</div>
</div>
);
}
}

View File

@@ -43,10 +43,10 @@ const MIN_LINES_NUMBER = 4;
const defaultReceipt = {
index: 0,
item_id: null,
rate: null,
item_id: '',
rate: '',
discount: 0,
quantity: null,
quantity: '',
description: '',
};

View File

@@ -111,7 +111,7 @@ function ReceiptsDataTable({
{
id: 'receipt_number',
Header: formatMessage({ id: 'receipt_number' }),
accessor: (row) => `#${row.receipt_number}`,
accessor: (row) => (row.receipt_number ? `#${row.receipt_number}` : null),
width: 140,
className: 'receipt_number',
},

View File

@@ -1,9 +1,15 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, Position, Classes } from '@blueprintjs/core';
import { FormGroup, ControlGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { MoneyInputGroup, CurrencySelectList, Row, Col } from 'components';
import {
MoneyInputGroup,
InputPrependText,
CurrencySelectList,
Row,
Col,
} from 'components';
import { FormattedMessage as T } from 'react-intl';
import withCurrencies from 'containers/Currencies/withCurrencies';
@@ -50,7 +56,12 @@ function VendorFinanicalPanelTab({
</FastField>
{/*------------ Opening balance -----------*/}
<FastField name={'opening_balance'}>
{({ field, field: { value }, meta: { error, touched } }) => (
{({
form: { values },
field,
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'opening_balance'} />}
className={classNames(
@@ -60,16 +71,19 @@ function VendorFinanicalPanelTab({
intent={inputIntent({ error, touched })}
inline={true}
>
<MoneyInputGroup
value={value}
onChange={field.onChange}
prefix={'$'}
inputGroupProps={{
fill: true,
...field,
}}
disabled={vendorId}
/>
<ControlGroup>
<InputPrependText text={values.currency_code } />
<MoneyInputGroup
value={value}
onChange={field.onChange}
prefix={'$'}
inputGroupProps={{
fill: true,
...field,
}}
disabled={vendorId}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>

View File

@@ -22,6 +22,7 @@ import VendorFloatingActions from './VendorFloatingActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withVendorDetail from './withVendorDetail';
import withVendorActions from './withVendorActions';
import withSettings from 'containers/Settings/withSettings';
import { compose, transformToForm } from 'utils';
@@ -73,6 +74,9 @@ function VendorForm({
requestSubmitVendor,
requestEditVendor,
// #withSettings
baseCurrency,
// #OwnProps
vendorId,
}) {
@@ -88,6 +92,7 @@ function VendorForm({
const initialValues = useMemo(
() => ({
...defaultInitialValues,
currency_code: baseCurrency,
...transformToForm(vendor, defaultInitialValues),
}),
[defaultInitialValues],
@@ -181,5 +186,8 @@ function VendorForm({
export default compose(
withVendorDetail(),
withDashboardActions,
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withVendorActions,
)(VendorForm);

View File

@@ -203,8 +203,8 @@ export default {
'The journal #{number} has been successfully edited.',
the_journal_has_been_successfully_deleted:
'The journal has been successfully deleted',
the_manual_journal_id_has_been_published:
'The manual journal id has been published',
the_manual_journal_has_been_published:
'The manual journal has been published',
the_journals_has_been_successfully_deleted:
'The journals has been successfully deleted ',
credit: 'Credit',
@@ -842,4 +842,6 @@ export default {
vendor_has_bills: 'Vendor has bills',
the_item_has_been_edited_successfully: 'The item has been edited successfully.',
you_cannot_make_payment_with_zero_total_amount: 'You cannot record payment transaction with zero total amount',
are_sure_to_publish_this_manual_journal:
'Are you sure you want to publish this manual journal?',
};