mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
Fix / takse expense & Journal
This commit is contained in:
@@ -34,6 +34,7 @@ const ERROR = {
|
||||
VENDORS_NOT_WITH_PAYABLE_ACCOUNT: 'VENDORS.NOT.WITH.PAYABLE.ACCOUNT',
|
||||
PAYABLE_ENTRIES_HAS_NO_VENDORS: 'PAYABLE.ENTRIES.HAS.NO.VENDORS',
|
||||
RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS: 'RECEIVABLE.ENTRIES.HAS.NO.CUSTOMERS',
|
||||
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,7 @@ function MakeJournalEntriesForm({
|
||||
onFormSubmit,
|
||||
onCancelForm,
|
||||
}) {
|
||||
|
||||
const { formatMessage } = useIntl();
|
||||
const {
|
||||
setFiles,
|
||||
@@ -68,10 +70,12 @@ function MakeJournalEntriesForm({
|
||||
saveCallback: requestSubmitMedia,
|
||||
deleteCallback: requestDeleteMedia,
|
||||
});
|
||||
|
||||
const handleDropFiles = useCallback((_files) => {
|
||||
setFiles(_files.filter((file) => file.uploaded === false));
|
||||
}, []);
|
||||
|
||||
|
||||
const savedMediaIds = useRef([]);
|
||||
const clearSavedMediaIds = () => {
|
||||
savedMediaIds.current = [];
|
||||
@@ -222,6 +226,15 @@ function MakeJournalEntriesForm({
|
||||
}),
|
||||
});
|
||||
}
|
||||
if(hasError(ERROR.CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO)){
|
||||
|
||||
AppToaster.show({
|
||||
message:formatMessage({
|
||||
id:'credit_debit_summation_should_not_equal_zero'
|
||||
}),
|
||||
intent:Intent.DANGER
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
|
||||
@@ -46,7 +46,7 @@ function MakeJournalEntriesPage({
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
history.push('/manual-journals');
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,7 +41,7 @@ function ManualJournalActionsBar({
|
||||
addManualJournalsTableQueries,
|
||||
|
||||
onFilterChanged,
|
||||
selectedRows,
|
||||
selectedRows = [],
|
||||
onBulkDelete,
|
||||
}) {
|
||||
const { path } = useRouteMatch();
|
||||
@@ -73,9 +73,9 @@ function ManualJournalActionsBar({
|
||||
onFilterChanged && onFilterChanged(filterConditions);
|
||||
},
|
||||
});
|
||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||
selectedRows,
|
||||
]);
|
||||
const hasSelectedRows = useMemo(
|
||||
() => selectedRows.length > 0,
|
||||
[selectedRows]);
|
||||
|
||||
// Handle delete button click.
|
||||
const handleBulkDelete = useCallback(() => {
|
||||
|
||||
@@ -23,12 +23,12 @@ import withManualJournals from 'containers/Accounting/withManualJournals';
|
||||
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||
|
||||
/**
|
||||
* Status column accessor.
|
||||
* Status column accessor.
|
||||
*/
|
||||
function StatusAccessor(row) {
|
||||
const StatusAccessor = (row) => {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={row.status}>
|
||||
<Choose.When condition={!!row.status}>
|
||||
<Tag minimal={true}>
|
||||
<T id={'published'} />
|
||||
</Tag>
|
||||
@@ -41,7 +41,7 @@ function StatusAccessor(row) {
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Note column accessor.
|
||||
@@ -115,12 +115,12 @@ function ManualJournalsDataTable({
|
||||
<Menu>
|
||||
<MenuItem text={formatMessage({ id: 'view_details' })} />
|
||||
<MenuDivider />
|
||||
{!journal.status && (
|
||||
<If condition={!journal.status}>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'publish_journal' })}
|
||||
onClick={handlePublishJournal(journal)}
|
||||
/>
|
||||
)}
|
||||
</If>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'edit_journal' })}
|
||||
onClick={handleEditJournal(journal)}
|
||||
@@ -230,20 +230,29 @@ function ManualJournalsDataTable({
|
||||
},
|
||||
[onSelectedRowsChange],
|
||||
);
|
||||
const selectionColumn = useMemo(
|
||||
() => ({
|
||||
minWidth: 40,
|
||||
width: 40,
|
||||
maxWidth: 40,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={manualJournalsCurrentPage}
|
||||
onFetchData={handleDataTableFetchData}
|
||||
manualSortBy={true}
|
||||
selectionColumn={true}
|
||||
noInitialFetch={true}
|
||||
selectionColumn={selectionColumn}
|
||||
expandable={true}
|
||||
sticky={true}
|
||||
loading={manualJournalsLoading && !isMounted}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
pagination={true}
|
||||
loading={manualJournalsLoading && !isMounted}
|
||||
rowContextMenu={onRowContextMenu}
|
||||
pagination={true}
|
||||
pagesCount={manualJournalsPagination.pagesCount}
|
||||
initialPageSize={manualJournalsTableQuery.page_size}
|
||||
initialPageIndex={manualJournalsTableQuery.page - 1}
|
||||
|
||||
@@ -3,7 +3,11 @@ import { Route, Switch, useHistory, withRouter } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
useIntl,
|
||||
FormattedHTMLMessage,
|
||||
} from 'react-intl';
|
||||
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
@@ -114,6 +118,7 @@ function ManualJournalsTable({
|
||||
const handleConfirmBulkDelete = useCallback(() => {
|
||||
requestDeleteBulkManualJournals(bulkDelete)
|
||||
.then(() => {
|
||||
setBulkDelete(false);
|
||||
AppToaster.show({
|
||||
message: formatMessage(
|
||||
{ id: 'the_journals_has_been_successfully_deleted' },
|
||||
@@ -121,7 +126,6 @@ function ManualJournalsTable({
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setBulkDelete(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setBulkDelete(false);
|
||||
@@ -181,6 +185,7 @@ function ManualJournalsTable({
|
||||
message: formatMessage({
|
||||
id: 'the_manual_journal_id_has_been_published',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -237,11 +242,7 @@ function ManualJournalsTable({
|
||||
onConfirm={handleConfirmManualJournalDelete}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={
|
||||
'once_delete_this_journal_category_you_will_able_to_restore_it'
|
||||
}
|
||||
/>
|
||||
<T id={'once_delete_this_journal_you_will_able_to_restore_it'} />
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
Alignment,
|
||||
Navbar,
|
||||
NavbarGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { pick, debounce } from 'lodash';
|
||||
|
||||
@@ -38,13 +34,14 @@ function AccountsViewsTabs({
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
|
||||
const history = useHistory();
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
changeAccountsCurrentView(customViewId || -1);
|
||||
setTopbarEditView(customViewId);
|
||||
changePageSubtitle((customViewId && viewItem) ? viewItem.name : '');
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
|
||||
addAccountsTableQueries({
|
||||
custom_view_id: customViewId,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Tag,
|
||||
} from '@blueprintjs/core';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { withRouter } from 'react-router';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import moment from 'moment';
|
||||
|
||||
@@ -28,6 +29,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
import withExpenses from 'containers/Expenses/withExpenses';
|
||||
import withExpensesActions from 'containers/Expenses/withExpensesActions';
|
||||
import withCurrentView from 'containers/Views/withCurrentView';
|
||||
|
||||
function ExpensesDataTable({
|
||||
//#withExpenes
|
||||
@@ -55,6 +57,10 @@ function ExpensesDataTable({
|
||||
const [initialMount, setInitialMount] = useState(false);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
useEffect(()=>{
|
||||
setInitialMount(false)
|
||||
},[customViewId])
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (!expensesLoading) {
|
||||
setInitialMount(true);
|
||||
@@ -260,7 +266,7 @@ function ExpensesDataTable({
|
||||
selectionColumn={true}
|
||||
noInitialFetch={true}
|
||||
sticky={true}
|
||||
loading={expensesLoading}
|
||||
loading={expensesLoading && !initialMount}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
rowContextMenu={onRowContextMenu}
|
||||
pagination={true}
|
||||
@@ -274,6 +280,8 @@ function ExpensesDataTable({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withCurrentView,
|
||||
withDialogActions,
|
||||
withDashboardActions,
|
||||
withExpensesActions,
|
||||
|
||||
@@ -28,6 +28,11 @@ import Dragzone from 'components/Dragzone';
|
||||
import useMedia from 'hooks/useMedia';
|
||||
import { compose, repeatValue } from 'utils';
|
||||
|
||||
const ERROR = {
|
||||
TOTAL_AMOUNT_EQUALS_ZERO: 'TOTAL.AMOUNT.EQUALS.ZERO',
|
||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||
};
|
||||
|
||||
function ExpenseForm({
|
||||
// #withMedia
|
||||
requestSubmitMedia,
|
||||
@@ -134,9 +139,7 @@ function ExpenseForm({
|
||||
description: '',
|
||||
reference_no: '',
|
||||
currency_code: '',
|
||||
categories: [
|
||||
...repeatValue(defaultCategory, 4),
|
||||
],
|
||||
categories: [...repeatValue(defaultCategory, 4)],
|
||||
}),
|
||||
[defaultCategory],
|
||||
);
|
||||
@@ -177,6 +180,30 @@ function ExpenseForm({
|
||||
: [];
|
||||
}, [expense]);
|
||||
|
||||
// Transform API errors in toasts messages.
|
||||
const transformErrors = (errors, { setErrors }) => {
|
||||
const hasError = (errorType) => errors.some((e) => e.type === errorType);
|
||||
if (hasError(ERROR.TOTAL_AMOUNT_EQUALS_ZERO)) {
|
||||
setErrors(
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'total_amount_equals_zero',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (hasError(ERROR.EXPENSE_ALREADY_PUBLISHED)) {
|
||||
setErrors(
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'expense_already_published',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema,
|
||||
@@ -214,16 +241,7 @@ function ExpenseForm({
|
||||
resetForm();
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.find((e) => e.type === 'TOTAL.AMOUNT.EQUALS.ZERO')) {
|
||||
}
|
||||
setErrors(
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'total_amount_equals_zero',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
}),
|
||||
);
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
});
|
||||
} else {
|
||||
@@ -244,6 +262,7 @@ function ExpenseForm({
|
||||
// resolve(response);
|
||||
})
|
||||
.catch((errors) => {
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
});
|
||||
}
|
||||
@@ -298,11 +317,9 @@ function ExpenseForm({
|
||||
const handleClearAllLines = () => {
|
||||
formik.setFieldValue(
|
||||
'categories',
|
||||
orderingCategoriesIndex([
|
||||
...repeatValue(defaultCategory, 4),
|
||||
]),
|
||||
);
|
||||
}
|
||||
orderingCategoriesIndex([...repeatValue(defaultCategory, 4)]),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'expense-form'}>
|
||||
@@ -316,7 +333,6 @@ function ExpenseForm({
|
||||
formik={formik}
|
||||
defaultRow={defaultCategory}
|
||||
/>
|
||||
|
||||
<div class="expense-form-footer">
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import {
|
||||
Alignment,
|
||||
Navbar,
|
||||
NavbarGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { pick, debounce } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
import withExpenses from './withExpenses';
|
||||
import withExpensesActions from './withExpensesActions';
|
||||
@@ -32,9 +29,13 @@ function ExpenseViewTabs({
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
|
||||
// props
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { custom_view_id: customViewId } = useParams();
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
changeExpensesView(customViewId || -1);
|
||||
@@ -51,6 +52,10 @@ function ExpenseViewTabs({
|
||||
};
|
||||
}, [customViewId, addExpensesTableQueries, changeExpensesView]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
onViewChanged && onViewChanged(customViewId);
|
||||
}, [customViewId]);
|
||||
|
||||
const debounceChangeHistory = useRef(
|
||||
debounce((toUrl) => {
|
||||
history.push(toUrl);
|
||||
@@ -59,7 +64,7 @@ function ExpenseViewTabs({
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
const toPath = viewId ? `${viewId}/custom_view` : '';
|
||||
debounceChangeHistory.current(`/expenses/${toPath}`);
|
||||
debounceChangeHistory.current(`/expenses-list/${toPath}`);
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
@@ -78,7 +83,7 @@ function ExpenseViewTabs({
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
<DashboardViewsTabs
|
||||
initialViewId={customViewId}
|
||||
baseUrl={'/expenses'}
|
||||
baseUrl={'/expenses-list'}
|
||||
tabs={tabs}
|
||||
onNewViewTabClick={handleClickNewView}
|
||||
onChange={handleTabsChange}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { Route, Switch, useHistory, useParams } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
@@ -45,6 +45,7 @@ function ExpensesList({
|
||||
const [deleteExpense, setDeleteExpense] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [bulkDelete, setBulkDelete] = useState(false);
|
||||
const [publishExpense, setPublishExpense] = useState(false);
|
||||
|
||||
const fetchResourceViews = useQuery(
|
||||
['resource-views', 'expenses'],
|
||||
@@ -155,17 +156,35 @@ function ExpensesList({
|
||||
[addExpensesTableQueries],
|
||||
);
|
||||
|
||||
const handlePublishExpense = useCallback(
|
||||
(expense) => {
|
||||
requestPublishExpense(expense.id).then(() => {
|
||||
// const fetchExpenses = useQuery(['expenses-table', expensesTableQuery], () =>
|
||||
// requestFetchExpensesTable(),
|
||||
// );
|
||||
|
||||
const handlePublishExpense = useCallback((expense) => {
|
||||
setPublishExpense(expense);
|
||||
}, []);
|
||||
|
||||
const handleCancelPublishExpense = useCallback(() => {
|
||||
setPublishExpense(false);
|
||||
});
|
||||
|
||||
// Handle publish expense confirm.
|
||||
const handleConfirmPublishExpense = useCallback(() => {
|
||||
requestPublishExpense(publishExpense.id)
|
||||
.then(() => {
|
||||
setPublishExpense(false);
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'the_expense_id_has_been_published' }),
|
||||
message: formatMessage({
|
||||
id: 'the_expense_has_been_published',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('expenses-table');
|
||||
})
|
||||
.catch((error) => {
|
||||
setPublishExpense(false);
|
||||
});
|
||||
fetchExpenses.refetch();
|
||||
},
|
||||
[requestPublishExpense, formatMessage],
|
||||
);
|
||||
}, [publishExpense, requestPublishExpense, formatMessage]);
|
||||
|
||||
// Handle selected rows change.
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
@@ -189,11 +208,11 @@ function ExpensesList({
|
||||
<DashboardPageContent>
|
||||
<Switch>
|
||||
<Route
|
||||
// exact={true}
|
||||
// path={[
|
||||
// '/expenses/:custom_view_id/custom_view',
|
||||
// '/expenses/new',
|
||||
// ]}
|
||||
exact={true}
|
||||
// path={[
|
||||
// '/expenses/:custom_view_id/custom_view',
|
||||
// 'expenses',
|
||||
// ]}
|
||||
>
|
||||
<ExpenseViewTabs />
|
||||
|
||||
@@ -239,14 +258,26 @@ function ExpensesList({
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'publish'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={publishExpense}
|
||||
onCancel={handleCancelPublishExpense}
|
||||
onConfirm={handleConfirmPublishExpense}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_publish_this_account'} />
|
||||
</p>
|
||||
</Alert>
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withExpensesActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withResourceActions,
|
||||
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
|
||||
|
||||
Reference in New Issue
Block a user