Fix / takse expense & Journal

This commit is contained in:
elforjani3
2020-07-05 21:30:51 +02:00
parent 679789345c
commit 7ea417d8c0
11 changed files with 176 additions and 73 deletions

View File

@@ -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({

View File

@@ -46,7 +46,7 @@ function MakeJournalEntriesPage({
);
const handleCancel = useCallback(() => {
history.push('/manual-journals');
history.goBack();
}, [history]);
return (

View File

@@ -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(() => {

View File

@@ -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}

View File

@@ -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>

View File

@@ -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,

View File

@@ -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,

View File

@@ -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'} />}

View File

@@ -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}

View File

@@ -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 })),

View File

@@ -441,7 +441,7 @@ export default {
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',
@@ -450,7 +450,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',
@@ -528,4 +528,27 @@ export default {
'A unique code/number for this account (limited to 10 characters)',
logic_expression: 'logic expression',
assign_to_customer: 'Assign to Customer',
are_sure_to_publish_this_account:
'Are you sure you want to publish this expense?',
once_delete_these_journalss_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 this account?<br /><br />If you're not sure, you can inactivate this account instead.`,
once_delete_this_journal_you_will_able_to_restore_it: `Once you delete this journal, you won\'t be able to restore the item later. Are you sure you want to delete ?`,
should_total_of_credit_and_debit_be_equal:
'should total of credit and debit be equal',
should_total_of_credit_and_debit_be_bigger_then_zero:
'should total of credit and debit be bigger then zero',
credit_debit_summation_should_not_equal_zero:
'credit debit summation should not equal zero',
entries_with_payable_account_no_assigned_with_vendors:
'entries with payable account no assigned with vendors',
entries_with_receivable_account_no_assigned_with_customers:
'entries with receivable account no assigned with customers',
vendors_should_assign_with_payable_account_only:
'vendors should assign with payable account only',
customers_should_assign_with_receivable_account_only:
'customers should assign with receivable account only',
expense_already_published: 'expense already published',
};