mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
Merge remote-tracking branch 'origin/task_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 = [];
|
||||
@@ -238,12 +242,15 @@ function MakeJournalEntriesForm({
|
||||
);
|
||||
}
|
||||
setErrors({ ...newErrors });
|
||||
AppToaster.show({
|
||||
message: toastMessages.map((message) => {
|
||||
return <div>- {message}</div>;
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
|
||||
if (toastMessages.length > 0) {
|
||||
AppToaster.show({
|
||||
message: toastMessages.map((message) => {
|
||||
return <div>- {message}</div>;
|
||||
}),
|
||||
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,9 +23,9 @@ 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}>
|
||||
@@ -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>
|
||||
|
||||
@@ -258,7 +259,7 @@ function ManualJournalsTable({
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={'once_delete_these_journalss_you_will_not_able_restore_them'}
|
||||
id={'once_delete_these_journals_you_will_not_able_restore_them'}
|
||||
/>
|
||||
</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,
|
||||
|
||||
@@ -29,6 +29,9 @@ import useMedia from 'hooks/useMedia';
|
||||
import { compose, repeatValue } from 'utils';
|
||||
|
||||
const MIN_LINES_NUMBER = 4;
|
||||
const ERROR = {
|
||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||
};
|
||||
|
||||
function ExpenseForm({
|
||||
// #withMedia
|
||||
@@ -187,6 +190,21 @@ 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.EXPENSE_ALREADY_PUBLISHED)) {
|
||||
setErrors(
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_expense_is_already_published',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema,
|
||||
@@ -237,16 +255,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 {
|
||||
@@ -265,6 +274,7 @@ function ExpenseForm({
|
||||
clearSavedMediaIds();
|
||||
})
|
||||
.catch((errors) => {
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
});
|
||||
}
|
||||
@@ -335,7 +345,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,18 +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(
|
||||
@@ -190,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 />
|
||||
|
||||
@@ -240,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_expense'} />
|
||||
</p>
|
||||
</Alert>
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withExpensesActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withResourceActions,
|
||||
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
|
||||
|
||||
@@ -440,7 +440,7 @@ export default {
|
||||
'The expenses #{number} have been successfully deleted',
|
||||
once_delete_these_expenses_you_will_not_able_restore_them:
|
||||
"Once you delete these expenses, you won't be able to retrieve them later. Are you sure you want to delete them?",
|
||||
the_expense_id_has_been_published: 'The expense id has been published',
|
||||
the_expense_has_been_published: 'The expense has been published',
|
||||
select_beneficiary_account: 'Select Beneficiary Account',
|
||||
total_amount_equals_zero: 'Total amount equals zero',
|
||||
value: 'Value',
|
||||
@@ -449,7 +449,7 @@ export default {
|
||||
as_date: 'As Date',
|
||||
aging_before_days: 'Aging before days',
|
||||
aging_periods: 'Aging periods',
|
||||
name_:'name',
|
||||
name_: 'name',
|
||||
as: 'As',
|
||||
receivable_aging_summary: 'Receivable Aging Summary',
|
||||
customers: 'Customers',
|
||||
@@ -536,5 +536,11 @@ export default {
|
||||
should_total_of_credit_and_debit_be_equal: 'Should total of credit and debit be equal.',
|
||||
no_accounts: 'No Accounts',
|
||||
the_accounts_have_been_successfully_inactivated: 'The accounts have been successfully inactivated.',
|
||||
account_code_is_not_unique: 'Account code is not unqiue.'
|
||||
account_code_is_not_unique: 'Account code is not unqiue.',
|
||||
are_sure_to_publish_this_expense:
|
||||
'Are you sure you want to publish this expense?',
|
||||
once_delete_these_journals_you_will_not_able_restore_them:
|
||||
"Once you delete these journalss, you won't be able to retrieve them later. Are you sure you want to delete them?",
|
||||
once_delete_this_journal_you_will_able_to_restore_it: `Once you delete this journal, you won\'t be able to restore it later. Are you sure you want to delete ?`,
|
||||
the_expense_is_already_published: 'The expense is already published.',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user