mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
feat: Edit make journal entries.
This commit is contained in:
@@ -1,29 +1,23 @@
|
|||||||
import React, {useMemo, useCallback, useState} from 'react';
|
import React, {useCallback, useState} from 'react';
|
||||||
import {omit} from 'lodash';
|
|
||||||
import {
|
import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
FormGroup,
|
|
||||||
Button,
|
Button,
|
||||||
Intent,
|
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import {Select} from '@blueprintjs/select';
|
import {Select} from '@blueprintjs/select';
|
||||||
// import MultiSelect from 'components/MultiSelect';
|
|
||||||
|
|
||||||
export default function AccountsMultiSelect({
|
export default function AccountsMultiSelect({
|
||||||
accounts,
|
accounts,
|
||||||
onAccountSelected,
|
onAccountSelected,
|
||||||
error,
|
error,
|
||||||
|
initialAccount,
|
||||||
}) {
|
}) {
|
||||||
const [selectedAccount, setSelectedAccount] = useState(null);
|
const [selectedAccount, setSelectedAccount] = useState(
|
||||||
|
initialAccount || null
|
||||||
|
);
|
||||||
// Account item of select accounts field.
|
// Account item of select accounts field.
|
||||||
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
|
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem text={item.name} label={item.code} key={item.id} onClick={handleClick} />
|
||||||
text={item.name}
|
|
||||||
label={item.code}
|
|
||||||
key={item.id}
|
|
||||||
onClick={handleClick} />
|
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -33,20 +27,17 @@ export default function AccountsMultiSelect({
|
|||||||
}, [setSelectedAccount, onAccountSelected]);
|
}, [setSelectedAccount, onAccountSelected]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
items={accounts}
|
items={accounts}
|
||||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||||
itemRenderer={accountItem}
|
itemRenderer={accountItem}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
filterable={true}
|
filterable={true}
|
||||||
onItemSelect={onAccountSelect}
|
onItemSelect={onAccountSelect}>
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
rightIcon='caret-down'
|
rightIcon='caret-down'
|
||||||
text={selectedAccount ? selectedAccount.name : 'Select account'}
|
text={selectedAccount ? selectedAccount.name : 'Select account'}
|
||||||
/>
|
/>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useCallback} from 'react';
|
import React, {useCallback, useMemo} from 'react';
|
||||||
import AccountsSelectList from 'components/AccountsSelectList';
|
import AccountsSelectList from 'components/AccountsSelectList';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
@@ -20,6 +20,10 @@ const AccountCellRenderer = ({
|
|||||||
|
|
||||||
const { account_id = false } = (errors[index] || {});
|
const { account_id = false } = (errors[index] || {});
|
||||||
|
|
||||||
|
const initialAccount = useMemo(() =>
|
||||||
|
accounts.find(a => a.id === initialValue),
|
||||||
|
[accounts, initialValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
intent={account_id ? Intent.DANGER : ''}
|
intent={account_id ? Intent.DANGER : ''}
|
||||||
@@ -31,7 +35,8 @@ const AccountCellRenderer = ({
|
|||||||
<AccountsSelectList
|
<AccountsSelectList
|
||||||
accounts={accounts}
|
accounts={accounts}
|
||||||
onAccountSelected={handleAccountSelected}
|
onAccountSelected={handleAccountSelected}
|
||||||
error={account_id} />
|
error={account_id}
|
||||||
|
initialAccount={initialAccount} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ export default function MoneyFieldGroup({
|
|||||||
|
|
||||||
const options = useMemo(() => ({
|
const options = useMemo(() => ({
|
||||||
prefix, suffix, thousands, decimal, precision,
|
prefix, suffix, thousands, decimal, precision,
|
||||||
}), []);
|
}), [
|
||||||
|
prefix, suffix, thousands, decimal, precision,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleChange = useCallback((event) => {
|
const handleChange = useCallback((event) => {
|
||||||
const formatted = formatter(event.target.value, options);
|
const formatted = formatter(event.target.value, options);
|
||||||
@@ -72,12 +74,12 @@ export default function MoneyFieldGroup({
|
|||||||
|
|
||||||
setState(formatted);
|
setState(formatted);
|
||||||
onChange && onChange(event, value);
|
onChange && onChange(event, value);
|
||||||
}, []);
|
}, [onChange, options]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const formatted = formatter(value, options);
|
const formatted = formatter(value, options);
|
||||||
setState(formatted)
|
setState(formatted)
|
||||||
}, []);
|
}, [value, options, setState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputGroup
|
<InputGroup
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
|
|
||||||
|
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import {
|
import {
|
||||||
makeJournalEntries,
|
makeJournalEntries,
|
||||||
|
fetchManualJournal,
|
||||||
|
editManualJournal,
|
||||||
} from 'store/accounting/accounting.actions';
|
} from 'store/accounting/accounting.actions';
|
||||||
import t from 'store/types';
|
import {
|
||||||
|
getManualJournal,
|
||||||
|
} from 'store/accounting/accounting.reducers';
|
||||||
|
|
||||||
export const mapStateToProps = (state, props) => ({
|
export const mapStateToProps = (state, props) => ({
|
||||||
|
getManualJournal: (id) => getManualJournal(state, id),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
makeJournalEntries: (form) => dispatch(makeJournalEntries({ form })),
|
requestMakeJournalEntries: (form) => dispatch(makeJournalEntries({ form })),
|
||||||
|
fetchManualJournal: (id) => dispatch(fetchManualJournal({ id })),
|
||||||
|
requestEditManualJournal: (id, form) => dispatch(editManualJournal({ id, form }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
export default connect(mapStateToProps, mapDispatchToProps);
|
||||||
@@ -13,21 +13,23 @@ import {compose} from 'utils';
|
|||||||
import useAsync from 'hooks/async';
|
import useAsync from 'hooks/async';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import {pick, omit} from 'lodash';
|
||||||
|
|
||||||
function MakeJournalEntriesForm({
|
function MakeJournalEntriesForm({
|
||||||
makeJournalEntries,
|
requestMakeJournalEntries,
|
||||||
fetchAccounts,
|
requestEditManualJournal,
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
|
changePageSubtitle,
|
||||||
|
editJournal,
|
||||||
}) {
|
}) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePageTitle('New Journal');
|
if (editJournal && editJournal.id) {
|
||||||
}, []);
|
changePageTitle('Edit Journal');
|
||||||
|
changePageSubtitle(`No. ${editJournal.journal_number}`);
|
||||||
const fetchHook = useAsync(async () => {
|
} else {
|
||||||
await Promise.all([
|
changePageTitle('New Journal');
|
||||||
fetchAccounts(),
|
}
|
||||||
]);
|
}, [changePageTitle, changePageSubtitle, editJournal]);
|
||||||
});
|
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
journal_number: Yup.string().required(),
|
journal_number: Yup.string().required(),
|
||||||
@@ -54,22 +56,31 @@ function MakeJournalEntriesForm({
|
|||||||
note: '',
|
note: '',
|
||||||
}), []);
|
}), []);
|
||||||
|
|
||||||
|
const initialValues = useMemo(() => ({
|
||||||
|
journal_number: '',
|
||||||
|
date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
description: '',
|
||||||
|
reference: '',
|
||||||
|
entries: [
|
||||||
|
defaultEntry,
|
||||||
|
defaultEntry,
|
||||||
|
defaultEntry,
|
||||||
|
defaultEntry,
|
||||||
|
],
|
||||||
|
}), [defaultEntry]);
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
validationSchema,
|
validationSchema,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
journal_number: '',
|
...(editJournal) ? {
|
||||||
date: moment(new Date()).format('YYYY-MM-DD'),
|
...pick(editJournal, Object.keys(initialValues)),
|
||||||
description: '',
|
entries: editJournal.entries.map((entry) => ({
|
||||||
reference: '',
|
...pick(entry, Object.keys(defaultEntry)),
|
||||||
entries: [
|
}))
|
||||||
defaultEntry,
|
} : {
|
||||||
defaultEntry,
|
...initialValues,
|
||||||
defaultEntry,
|
}
|
||||||
defaultEntry,
|
|
||||||
defaultEntry,
|
|
||||||
defaultEntry,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
onSubmit: (values, actions) => {
|
onSubmit: (values, actions) => {
|
||||||
const form = values.entries.filter((entry) => (
|
const form = values.entries.filter((entry) => (
|
||||||
@@ -87,18 +98,31 @@ function MakeJournalEntriesForm({
|
|||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'credit_and_debit_not_equal',
|
message: 'credit_and_debit_not_equal',
|
||||||
});
|
});
|
||||||
|
actions.setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeJournalEntries({ ...values, entries: form })
|
if (editJournal && editJournal.id) {
|
||||||
.then((response) => {
|
requestEditManualJournal(editJournal.id, { ...values, entries: form })
|
||||||
AppToaster.show({
|
.then((response) => {
|
||||||
message: 'manual_journal_has_been_submit',
|
AppToaster.show({
|
||||||
});
|
message: 'manual_journal_has_been_edited',
|
||||||
actions.setSubmitting(false);
|
});
|
||||||
}).catch((error) => {
|
actions.setSubmitting(false);
|
||||||
actions.setSubmitting(false);
|
}).catch((error) => {
|
||||||
});
|
actions.setSubmitting(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
requestMakeJournalEntries({ ...values, entries: form })
|
||||||
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'manual_journal_has_been_submit',
|
||||||
|
});
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
}).catch((error) => {
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ export default function MakeJournalEntriesHeader({
|
|||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleDateChange = (date) => {
|
const handleDateChange = useCallback((date) => {
|
||||||
const formatted = moment(date).format('YYYY-MM-DD');
|
const formatted = moment(date).format('YYYY-MM-DD');
|
||||||
formik.setFieldValue('date', formatted);
|
formik.setFieldValue('date', formatted);
|
||||||
};
|
}, [formik]);
|
||||||
|
|
||||||
const infoIcon = useMemo(() => (<Icon icon="info-circle" iconSize={12} />), []);
|
const infoIcon = useMemo(() =>
|
||||||
|
(<Icon icon="info-circle" iconSize={12} />), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="make-journal-entries__header">
|
<div class="make-journal-entries__header">
|
||||||
|
|||||||
@@ -1,14 +1,39 @@
|
|||||||
import React from 'react';
|
import React, {useMemo} from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { useAsync } from 'react-use';
|
||||||
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
||||||
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
import DashboardConnect from 'connectors/Dashboard.connector';
|
||||||
import {compose} from 'utils';
|
import {compose} from 'utils';
|
||||||
|
import MakeJournalEntriesConnect from 'connectors/MakeJournalEntries.connect';
|
||||||
|
import AccountsConnect from 'connectors/Accounts.connector';
|
||||||
|
|
||||||
|
function MakeJournalEntriesPage({
|
||||||
|
fetchManualJournal,
|
||||||
|
getManualJournal,
|
||||||
|
fetchAccounts,
|
||||||
|
}) {
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const fetchJournal = useAsync(() => {
|
||||||
|
return Promise.all([
|
||||||
|
fetchAccounts(),
|
||||||
|
(id) && fetchManualJournal(id),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
const editJournal = useMemo(() =>
|
||||||
|
getManualJournal(id) || null,
|
||||||
|
[getManualJournal, id]);
|
||||||
|
|
||||||
function MakeJournalEntriesPage() {
|
|
||||||
return (
|
return (
|
||||||
<MakeJournalEntriesForm />
|
<LoadingIndicator loading={fetchJournal.loading} mount={false}>
|
||||||
|
<MakeJournalEntriesForm editJournal={editJournal} />
|
||||||
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DashboardConnect,
|
DashboardConnect,
|
||||||
|
AccountsConnect,
|
||||||
|
MakeJournalEntriesConnect,
|
||||||
)(MakeJournalEntriesPage);
|
)(MakeJournalEntriesPage);
|
||||||
@@ -70,6 +70,9 @@ const NoteCellRenderer = (chainedComponent) => (props) => {
|
|||||||
return chainedComponent(props);
|
return chainedComponent(props);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make journal entries table component.
|
||||||
|
*/
|
||||||
function MakeJournalEntriesTable({
|
function MakeJournalEntriesTable({
|
||||||
formik,
|
formik,
|
||||||
accounts,
|
accounts,
|
||||||
@@ -79,6 +82,8 @@ function MakeJournalEntriesTable({
|
|||||||
}) {
|
}) {
|
||||||
const [rows, setRow] = useState([
|
const [rows, setRow] = useState([
|
||||||
...formik.values.entries.map((e) => ({ ...e, rowType: 'editor'})),
|
...formik.values.entries.map((e) => ({ ...e, rowType: 'editor'})),
|
||||||
|
defaultRow,
|
||||||
|
defaultRow,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Handles update datatable data.
|
// Handles update datatable data.
|
||||||
@@ -101,11 +106,15 @@ function MakeJournalEntriesTable({
|
|||||||
// Handles click remove datatable row.
|
// Handles click remove datatable row.
|
||||||
const handleRemoveRow = useCallback((rowIndex) => {
|
const handleRemoveRow = useCallback((rowIndex) => {
|
||||||
const removeIndex = parseInt(rowIndex, 10);
|
const removeIndex = parseInt(rowIndex, 10);
|
||||||
setRow([
|
const newRows = rows.filter((row, index) => index !== removeIndex);
|
||||||
...rows.filter((row, index) => index !== removeIndex),
|
|
||||||
]);
|
setRow([ ...newRows ]);
|
||||||
|
formik.setFieldValue('entries', newRows
|
||||||
|
.filter(row => row.rowType === 'editor')
|
||||||
|
.map(row => ({ ...omit(row, ['rowType']) })
|
||||||
|
));
|
||||||
onClickRemoveRow && onClickRemoveRow(removeIndex);
|
onClickRemoveRow && onClickRemoveRow(removeIndex);
|
||||||
}, [rows, onClickRemoveRow]);
|
}, [rows, formik, onClickRemoveRow]);
|
||||||
|
|
||||||
// Memorized data table columns.
|
// Memorized data table columns.
|
||||||
const columns = useMemo(() => [
|
const columns = useMemo(() => [
|
||||||
@@ -123,7 +132,7 @@ function MakeJournalEntriesTable({
|
|||||||
{
|
{
|
||||||
Header: 'Account',
|
Header: 'Account',
|
||||||
id: 'account_id',
|
id: 'account_id',
|
||||||
accessor: 'account',
|
accessor: 'account_id',
|
||||||
Cell: TotalAccountCellRenderer(AccountsListFieldCell),
|
Cell: TotalAccountCellRenderer(AccountsListFieldCell),
|
||||||
className: "account",
|
className: "account",
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ export default [
|
|||||||
text: 'Make Journal Entry'
|
text: 'Make Journal Entry'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: `${BASE_URL}/accounting/manual-journal/:id`,
|
||||||
|
name: 'dashboard.manual.journal.edit',
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () =>
|
||||||
|
import('containers/Dashboard/Accounting/MakeJournalEntriesPage')
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/items/list`,
|
path: `${BASE_URL}/items/list`,
|
||||||
|
|||||||
@@ -7,4 +7,27 @@ export const makeJournalEntries = ({ form }) => {
|
|||||||
resolve(response);
|
resolve(response);
|
||||||
}).catch((error) => { reject(error); });
|
}).catch((error) => { reject(error); });
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchManualJournal = ({ id }) => {
|
||||||
|
return (dispatch) => new Promise((resolve, reject) => {
|
||||||
|
ApiService.get(`accounting/manual-journals/${id}`).then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.MANUAL_JOURNAL_SET,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
manualJournal: response.data.manual_journal,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
}).catch((error) => { reject(error); });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editManualJournal = ({ form, id }) => {
|
||||||
|
return (dispatch) => new Promise((resolve, reject) => {
|
||||||
|
ApiService.post(`accounting/manual-journals/${id}`, form).then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
}).catch((error) => { reject(error); });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import t from 'store/types';
|
||||||
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
manualJournals: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createReducer(initialState, {
|
||||||
|
|
||||||
|
[t.MANUAL_JOURNAL_SET]: (state, action) => {
|
||||||
|
const { id, manualJournal } = action.payload;
|
||||||
|
state.manualJournals[id] = manualJournal;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const getManualJournal = (state, id) => {
|
||||||
|
return state.accounting.manualJournals[id];
|
||||||
|
}
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
MAKE_JOURNAL_ENTRIES: 'MAKE_JOURNAL_ENTRIES',
|
MAKE_JOURNAL_ENTRIES: 'MAKE_JOURNAL_ENTRIES',
|
||||||
|
MANUAL_JOURNAL_SET: 'MANUAL_JOURNAL_SET',
|
||||||
}
|
}
|
||||||
@@ -13,12 +13,14 @@ import resources from './resources/resources.reducer';
|
|||||||
import financialStatements from './financialStatement/financialStatements.reducer';
|
import financialStatements from './financialStatement/financialStatements.reducer';
|
||||||
import itemCategories from './itemCategories/itemsCateory.reducer';
|
import itemCategories from './itemCategories/itemsCateory.reducer';
|
||||||
import settings from './settings/settings.reducer';
|
import settings from './settings/settings.reducer';
|
||||||
|
import accounting from './accounting/accounting.reducers';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
authentication,
|
authentication,
|
||||||
dashboard,
|
dashboard,
|
||||||
users,
|
users,
|
||||||
accounts,
|
accounts,
|
||||||
|
accounting,
|
||||||
fields,
|
fields,
|
||||||
views,
|
views,
|
||||||
expenses,
|
expenses,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import authentication from './authentication/authentication.types';
|
import authentication from './authentication/authentication.types';
|
||||||
import accounts from './accounts/accounts.types';
|
import accounts from './accounts/accounts.types';
|
||||||
|
import accounting from './accounting/accounting.types'
|
||||||
import currencies from './currencies/currencies.types';
|
import currencies from './currencies/currencies.types';
|
||||||
import customFields from './customFields/customFields.types';
|
import customFields from './customFields/customFields.types';
|
||||||
import customViews from './customViews/customViews.types';
|
import customViews from './customViews/customViews.types';
|
||||||
@@ -27,5 +28,6 @@ export default {
|
|||||||
...users,
|
...users,
|
||||||
...financialStatements,
|
...financialStatements,
|
||||||
...itemCategories,
|
...itemCategories,
|
||||||
...settings
|
...settings,
|
||||||
|
...accounting,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export default {
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
router.use(JWTAuth);
|
router.use(JWTAuth);
|
||||||
|
|
||||||
|
router.get('/manual-journals/:id',
|
||||||
|
this.getManualJournal.validation,
|
||||||
|
asyncMiddleware(this.getManualJournal.handler));
|
||||||
|
|
||||||
router.get('/manual-journals',
|
router.get('/manual-journals',
|
||||||
this.manualJournals.validation,
|
this.manualJournals.validation,
|
||||||
asyncMiddleware(this.manualJournals.handler));
|
asyncMiddleware(this.manualJournals.handler));
|
||||||
@@ -33,7 +37,7 @@ export default {
|
|||||||
this.makeJournalEntries.validation,
|
this.makeJournalEntries.validation,
|
||||||
asyncMiddleware(this.makeJournalEntries.handler));
|
asyncMiddleware(this.makeJournalEntries.handler));
|
||||||
|
|
||||||
router.post('/manual-journal/:id',
|
router.post('/manual-journals/:id',
|
||||||
this.editManualJournal.validation,
|
this.editManualJournal.validation,
|
||||||
asyncMiddleware(this.editManualJournal.handler));
|
asyncMiddleware(this.editManualJournal.handler));
|
||||||
|
|
||||||
@@ -375,6 +379,26 @@ export default {
|
|||||||
journal.loadEntries(transactions);
|
journal.loadEntries(transactions);
|
||||||
journal.removeEntries();
|
journal.removeEntries();
|
||||||
|
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const account = accounts.find((a) => a.id === entry.account_id);
|
||||||
|
|
||||||
|
const jouranlEntry = new JournalEntry({
|
||||||
|
debit: entry.debit,
|
||||||
|
credit: entry.credit,
|
||||||
|
account: account.id,
|
||||||
|
referenceType: 'Journal',
|
||||||
|
referenceId: manualJournal.id,
|
||||||
|
accountNormal: account.type.normal,
|
||||||
|
note: entry.note,
|
||||||
|
date: formattedDate,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
if (entry.debit) {
|
||||||
|
journal.debit(jouranlEntry);
|
||||||
|
} else {
|
||||||
|
journal.credit(jouranlEntry);
|
||||||
|
}
|
||||||
|
});
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveEntries(),
|
journal.saveEntries(),
|
||||||
@@ -405,8 +429,19 @@ export default {
|
|||||||
return res.status(404).send({
|
return res.status(404).send({
|
||||||
errors: [{ type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100 }],
|
errors: [{ type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const transactions = await AccountTransaction.query()
|
||||||
|
.whereIn('reference_type', ['Journal', 'ManualJournal'])
|
||||||
|
.where('reference_id', manualJournal.id);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
manual_journal: {
|
||||||
|
...manualJournal.toJSON(),
|
||||||
|
entries: [
|
||||||
|
...transactions,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ describe('routes: /accounts/', () => {
|
|||||||
.post('/api/accounts')
|
.post('/api/accounts')
|
||||||
.set('x-access-token', loginRes.body.token)
|
.set('x-access-token', loginRes.body.token)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
expect(res.status).equals(422);
|
expect(res.status).equals(422);
|
||||||
expect(res.body.code).equals('validation_error');
|
expect(res.body.code).equals('validation_error');
|
||||||
});
|
});
|
||||||
@@ -191,7 +190,6 @@ describe('routes: /accounts/', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET: `/accounts`', () => {
|
describe('GET: `/accounts`', () => {
|
||||||
|
|
||||||
it('Should retrieve accounts resource not found.', async () => {
|
it('Should retrieve accounts resource not found.', async () => {
|
||||||
const res = await request()
|
const res = await request()
|
||||||
.get('/api/accounts')
|
.get('/api/accounts')
|
||||||
|
|||||||
Reference in New Issue
Block a user