mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
- feat: Filter expense and payment accounts on expense form.
- feat: Make journal errors with receivable and payable accounts. - fix: Handle database big numbers. - fix: Indexing lines when add a new line on make journal form. - fix: Abstruct accounts type component.
This commit is contained in:
@@ -154,7 +154,7 @@ function ExpensesDataTable({
|
||||
{
|
||||
id: 'payment_date',
|
||||
Header: formatMessage({ id: 'payment_date' }),
|
||||
accessor: () => moment().format('YYYY MMM DD'),
|
||||
accessor: (r) => moment(r.payment_date).format('YYYY MMM DD'),
|
||||
width: 140,
|
||||
className: 'payment_date',
|
||||
},
|
||||
|
||||
@@ -12,8 +12,8 @@ export default function ExpenseFloatingFooter({
|
||||
return (
|
||||
<div className={'form__floating-footer'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
onSubmitClick({ publish: true, redirect: true });
|
||||
|
||||
@@ -28,6 +28,8 @@ import Dragzone from 'components/Dragzone';
|
||||
import useMedia from 'hooks/useMedia';
|
||||
import { compose, repeatValue } from 'utils';
|
||||
|
||||
const MIN_LINES_NUMBER = 4;
|
||||
|
||||
function ExpenseForm({
|
||||
// #withMedia
|
||||
requestSubmitMedia,
|
||||
@@ -81,22 +83,26 @@ function ExpenseForm({
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
beneficiary: Yup.string().label(formatMessage({ id: 'beneficiary' })),
|
||||
payment_account_id: Yup.string()
|
||||
payment_account_id: Yup.number()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'payment_account_' })),
|
||||
payment_date: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'payment_date_' })),
|
||||
reference_no: Yup.string(),
|
||||
currency_code: Yup.string().label(formatMessage({ id: 'currency_code' })),
|
||||
reference_no: Yup.string().min(1).max(255),
|
||||
currency_code: Yup.string()
|
||||
.nullable()
|
||||
.label(formatMessage({ id: 'currency_code' })),
|
||||
description: Yup.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(1024)
|
||||
.label(formatMessage({ id: 'description' })),
|
||||
publish: Yup.boolean().label(formatMessage({ id: 'publish' })),
|
||||
categories: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
index: Yup.number().nullable(),
|
||||
amount: Yup.number().nullable(),
|
||||
index: Yup.number().min(1).max(1000).nullable(),
|
||||
amount: Yup.number().decimalScale(13).nullable(),
|
||||
expense_account_id: Yup.number()
|
||||
.nullable()
|
||||
.when(['amount'], {
|
||||
@@ -134,9 +140,7 @@ function ExpenseForm({
|
||||
description: '',
|
||||
reference_no: '',
|
||||
currency_code: '',
|
||||
categories: [
|
||||
...repeatValue(defaultCategory, 4),
|
||||
],
|
||||
categories: [...repeatValue(defaultCategory, MIN_LINES_NUMBER)],
|
||||
}),
|
||||
[defaultCategory],
|
||||
);
|
||||
@@ -153,9 +157,15 @@ function ExpenseForm({
|
||||
...(expense
|
||||
? {
|
||||
...pick(expense, Object.keys(defaultInitialValues)),
|
||||
categories: expense.categories.map((category) => ({
|
||||
...pick(category, Object.keys(defaultCategory)),
|
||||
})),
|
||||
categories: [
|
||||
...expense.categories.map((category) => ({
|
||||
...pick(category, Object.keys(defaultCategory)),
|
||||
})),
|
||||
...repeatValue(
|
||||
defaultCategory,
|
||||
Math.max(MIN_LINES_NUMBER - expense.categories.length, 0),
|
||||
),
|
||||
],
|
||||
}
|
||||
: {
|
||||
...defaultInitialValues,
|
||||
@@ -184,6 +194,20 @@ function ExpenseForm({
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||
setSubmitting(true);
|
||||
const totalAmount = values.categories.reduce((total, item) => {
|
||||
return total + item.amount;
|
||||
}, 0);
|
||||
|
||||
if (totalAmount <= 0) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'amount_cannot_be_zero_or_empty',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const categories = values.categories.filter(
|
||||
(category) =>
|
||||
category.amount && category.index && category.expense_account_id,
|
||||
@@ -193,7 +217,6 @@ function ExpenseForm({
|
||||
publish: payload.publish,
|
||||
categories,
|
||||
};
|
||||
|
||||
const saveExpense = (mdeiaIds) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const requestForm = { ...form, media_ids: mdeiaIds };
|
||||
@@ -237,11 +260,9 @@ function ExpenseForm({
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
formik.resetForm();
|
||||
resetForm();
|
||||
saveInvokeSubmit({ action: 'new', ...payload });
|
||||
clearSavedMediaIds();
|
||||
|
||||
// resolve(response);
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
@@ -298,11 +319,9 @@ function ExpenseForm({
|
||||
const handleClearAllLines = () => {
|
||||
formik.setFieldValue(
|
||||
'categories',
|
||||
orderingCategoriesIndex([
|
||||
...repeatValue(defaultCategory, 4),
|
||||
]),
|
||||
);
|
||||
}
|
||||
orderingCategoriesIndex([...repeatValue(defaultCategory, MIN_LINES_NUMBER)]),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'expense-form'}>
|
||||
@@ -324,7 +343,6 @@ function ExpenseForm({
|
||||
>
|
||||
<TextArea
|
||||
growVertically={true}
|
||||
large={true}
|
||||
{...formik.getFieldProps('description')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@@ -61,36 +61,6 @@ function ExpenseFormHeader({
|
||||
}
|
||||
};
|
||||
|
||||
// Account item of select accounts field.
|
||||
const accountItem = (item, { handleClick }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={item.id}
|
||||
text={item.name}
|
||||
label={item.code}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Filters accounts items.
|
||||
// @filter accounts predicator resauble
|
||||
const filterAccountsPredicater = useCallback(
|
||||
(query, account, _index, exactMatch) => {
|
||||
const normalizedTitle = account.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return (
|
||||
`${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0
|
||||
);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
// Handles change account.
|
||||
const onChangeAccount = useCallback(
|
||||
(account) => {
|
||||
@@ -112,6 +82,12 @@ function ExpenseFormHeader({
|
||||
[setFieldValue, selectedItems],
|
||||
);
|
||||
|
||||
// Filter payment accounts.
|
||||
const paymentAccounts = useMemo(
|
||||
() => accountsList.filter(a => a?.type?.key === 'current_asset'),
|
||||
[accountsList],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'dashboard__insider--expense-form__header'}>
|
||||
<Row>
|
||||
@@ -165,7 +141,7 @@ function ExpenseFormHeader({
|
||||
}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accountsList}
|
||||
accounts={paymentAccounts}
|
||||
onAccountSelected={onChangeAccount}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
selectedAccountId={values.payment_account_id}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
} from 'components/DataTableCells';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
|
||||
|
||||
const ExpenseCategoryHeaderCell = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -22,10 +21,10 @@ const ExpenseCategoryHeaderCell = () => {
|
||||
<Hint />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Actions cell renderer.
|
||||
const ActionsCellRenderer = ({
|
||||
// Actions cell renderer.
|
||||
const ActionsCellRenderer = ({
|
||||
row: { index },
|
||||
column: { id },
|
||||
cell: { value: initialValue },
|
||||
@@ -101,16 +100,11 @@ function ExpenseTable({
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
setRows([
|
||||
...categories.map((e) => ({ ...e, rowType: 'editor' })),
|
||||
]);
|
||||
setRows([...categories.map((e) => ({ ...e, rowType: 'editor' }))]);
|
||||
}, [categories]);
|
||||
|
||||
// Final table rows editor rows and total and final blank row.
|
||||
const tableRows = useMemo(
|
||||
() => [...rows, { rowType: 'total' }],
|
||||
[rows],
|
||||
);
|
||||
const tableRows = useMemo(() => [...rows, { rowType: 'total' }], [rows]);
|
||||
|
||||
// Memorized data table columns.
|
||||
const columns = useMemo(
|
||||
@@ -133,6 +127,7 @@ function ExpenseTable({
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 250,
|
||||
accountsDataProp: 'expenseAccounts',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'amount_currency' }, { currency: 'USD' }),
|
||||
@@ -187,6 +182,11 @@ function ExpenseTable({
|
||||
// Handles click remove datatable row.
|
||||
const handleRemoveRow = useCallback(
|
||||
(rowIndex) => {
|
||||
// Can't continue if there is just one row line or less.
|
||||
if (rows.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const removeIndex = parseInt(rowIndex, 10);
|
||||
const newRows = rows.filter((row, index) => index !== removeIndex);
|
||||
|
||||
@@ -220,6 +220,12 @@ function ExpenseTable({
|
||||
[rows],
|
||||
);
|
||||
|
||||
// Filter expense accounts.
|
||||
const expenseAccounts = useMemo(
|
||||
() => accountsList.filter((a) => a?.type?.root_type === 'expense'),
|
||||
[accountsList],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'dashboard__insider--expense-form__table'}>
|
||||
<DataTable
|
||||
@@ -229,6 +235,7 @@ function ExpenseTable({
|
||||
sticky={true}
|
||||
payload={{
|
||||
accounts: accountsList,
|
||||
expenseAccounts,
|
||||
errors: errors.categories || [],
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
|
||||
@@ -109,7 +109,7 @@ function ExpensesList({
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: formatMessage(
|
||||
{ id: 'the_expenses_has_been_successfully_deleted' },
|
||||
{ id: 'the_expenses_have_been_successfully_deleted' },
|
||||
{ count: selectedRowsCount },
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
@@ -160,6 +160,7 @@ function ExpensesList({
|
||||
requestPublishExpense(expense.id).then(() => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'the_expense_id_has_been_published' }),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
fetchExpenses.refetch();
|
||||
|
||||
@@ -5,7 +5,7 @@ export default () => {
|
||||
const getExpenseById = getExpenseByIdFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
expenseDetail: getExpenseById(state, props),
|
||||
expense: getExpenseById(state, props),
|
||||
});
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
Reference in New Issue
Block a user