diff --git a/client/src/components/JournalEntry/ManualJournalActionsBar.js b/client/src/components/JournalEntry/ManualJournalActionsBar.js new file mode 100644 index 000000000..dbdd4c815 --- /dev/null +++ b/client/src/components/JournalEntry/ManualJournalActionsBar.js @@ -0,0 +1,111 @@ +import React, { useMemo, useState } from 'react'; +import Icon from 'components/Icon'; +import { + Button, + NavbarGroup, + Classes, + NavbarDivider, + MenuItem, + Menu, + Popover, + PopoverInteractionKind, + Position, + Intent +} from '@blueprintjs/core'; +import classNames from 'classnames'; +import { useRouteMatch, useHistory } from 'react-router-dom'; +import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; +import DialogConnect from 'connectors/Dialog.connector'; +import { compose } from 'utils'; +import FilterDropdown from 'components/FilterDropdown'; +import ManualJournalsConnect from 'connectors/ManualJournals.connect'; +import ResourceConnect from 'connectors/Resource.connector'; + +function ManualJournalActionsBar({ + views, + getResourceFields, + addManualJournalsTableQueries, + onFilterChanged +}) { + const { path } = useRouteMatch(); + const history = useHistory(); + + const manualJournalFields = getResourceFields('manual_journals'); + const viewsMenuItems = views.map(view => { + return ( + + ); + }); + + const onClickNewManualJournal = () => { + history.push('/dashboard/accounting/make-journal-entry'); + }; + const filterDropdown = FilterDropdown({ + fields: manualJournalFields, + onFilterChange: filterConditions => { + addManualJournalsTableQueries({ + filter_roles: filterConditions || '' + }); + onFilterChanged && onFilterChanged(filterConditions); + } + }); + return ( + + + {viewsMenuItems}} + minimal={true} + interactionKind={PopoverInteractionKind.HOVER} + position={Position.BOTTOM_LEFT} + > + diff --git a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesForm.js b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesForm.js index c32079c79..ea75b5563 100644 --- a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesForm.js +++ b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesForm.js @@ -1,6 +1,4 @@ - - -import React, {useMemo, useEffect} from 'react'; +import React, {useMemo, useState, useEffect, useCallback} from 'react'; import * as Yup from 'yup'; import MakeJournalEntriesHeader from './MakeJournalEntriesHeader'; import MakeJournalEntriesFooter from './MakeJournalEntriesFooter'; @@ -10,10 +8,9 @@ import MakeJournalEntriesConnect from 'connectors/MakeJournalEntries.connect'; import AccountsConnect from 'connectors/Accounts.connector'; import DashboardConnect from 'connectors/Dashboard.connector'; import {compose} from 'utils'; -import useAsync from 'hooks/async'; import moment from 'moment'; import AppToaster from 'components/AppToaster'; -import {pick, omit} from 'lodash'; +import {pick} from 'lodash'; function MakeJournalEntriesForm({ requestMakeJournalEntries, @@ -21,6 +18,8 @@ function MakeJournalEntriesForm({ changePageTitle, changePageSubtitle, editJournal, + onFormSubmit, + onCancelForm, }) { useEffect(() => { if (editJournal && editJournal.id) { @@ -49,6 +48,12 @@ function MakeJournalEntriesForm({ ) }); + const saveInvokeSubmit = useCallback((payload) => { + onFormSubmit && onFormSubmit(payload) + }, [onFormSubmit]); + + const [payload, setPayload] = useState({}); + const defaultEntry = useMemo(() => ({ account_id: null, credit: 0, @@ -83,11 +88,11 @@ function MakeJournalEntriesForm({ } }, onSubmit: (values, actions) => { - const form = values.entries.filter((entry) => ( + const entries = values.entries.filter((entry) => ( (entry.credit || entry.debit) )); const getTotal = (type = 'credit') => { - return form.reduce((total, item) => { + return entries.reduce((total, item) => { return item[type] ? item[type] + total : total; }, 0); } @@ -101,24 +106,27 @@ function MakeJournalEntriesForm({ actions.setSubmitting(false); return; } - + const form = { ...values, status: payload.publish, entries }; + if (editJournal && editJournal.id) { - requestEditManualJournal(editJournal.id, { ...values, entries: form }) + requestEditManualJournal(editJournal.id, form) .then((response) => { AppToaster.show({ message: 'manual_journal_has_been_edited', }); actions.setSubmitting(false); + saveInvokeSubmit({ action: 'update', ...payload }); }).catch((error) => { actions.setSubmitting(false); }); } else { - requestMakeJournalEntries({ ...values, entries: form }) + requestMakeJournalEntries(form) .then((response) => { AppToaster.show({ message: 'manual_journal_has_been_submit', }); actions.setSubmitting(false); + saveInvokeSubmit({ action: 'new', ...payload }); }).catch((error) => { actions.setSubmitting(false); }); @@ -126,12 +134,24 @@ function MakeJournalEntriesForm({ }, }); + const handleSubmitClick = useCallback((payload) => { + setPayload(payload); + formik.handleSubmit(); + }, [setPayload, formik]); + + const handleCancelClick = useCallback((payload) => { + onCancelForm && onCancelForm(payload); + }, [onCancelForm]); + return (
- +
); diff --git a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesHeader.js b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesHeader.js index 761c8f696..8f5e5cbaf 100644 --- a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesHeader.js +++ b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesHeader.js @@ -1,13 +1,11 @@ import React, {useMemo, useCallback} from 'react'; -import * as Yup from 'yup'; import { InputGroup, FormGroup, Intent, Position, } from '@blueprintjs/core'; -import {DatePicker, DateInput} from '@blueprintjs/datetime'; -import {Formik, useFormik} from "formik"; +import {DateInput} from '@blueprintjs/datetime'; import {useIntl} from 'react-intl'; import {Row, Col} from 'react-grid-system'; import moment from 'moment'; diff --git a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesPage.js b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesPage.js index 5d05a85ce..ebd59ddbd 100644 --- a/client/src/containers/Dashboard/Accounting/MakeJournalEntriesPage.js +++ b/client/src/containers/Dashboard/Accounting/MakeJournalEntriesPage.js @@ -1,5 +1,5 @@ -import React, {useMemo} from 'react'; -import { useParams } from 'react-router-dom'; +import React, {useMemo, useCallback} from 'react'; +import { useParams, useHistory } from 'react-router-dom'; import { useAsync } from 'react-use'; import MakeJournalEntriesForm from './MakeJournalEntriesForm'; import LoadingIndicator from 'components/LoadingIndicator'; @@ -13,6 +13,7 @@ function MakeJournalEntriesPage({ getManualJournal, fetchAccounts, }) { + const history = useHistory(); const { id } = useParams(); const fetchJournal = useAsync(() => { @@ -25,9 +26,21 @@ function MakeJournalEntriesPage({ getManualJournal(id) || null, [getManualJournal, id]); + const handleFormSubmit = useCallback((payload) => { + payload.redirect && + history.push('/dashboard/accounting/manual-journals'); + }, [history]); + + const handleCancel = useCallback(() => { + history.push('/dashboard/accounting/manual-journals'); + }, [history]); + return ( - + ); } diff --git a/client/src/containers/Dashboard/Accounting/ManualJournalsTable.js b/client/src/containers/Dashboard/Accounting/ManualJournalsTable.js new file mode 100644 index 000000000..87bc75308 --- /dev/null +++ b/client/src/containers/Dashboard/Accounting/ManualJournalsTable.js @@ -0,0 +1,126 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { Route, Switch, useHistory } from 'react-router-dom'; +import useAsync from 'hooks/async'; +import { Alert, Intent } from '@blueprintjs/core'; +import AppToaster from 'components/AppToaster'; +import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; +import DashboardInsider from 'components/Dashboard/DashboardInsider'; +import ManualJournalsViewTabs from 'components/JournalEntry/ManualJournalsViewTabs'; +import ManualJournalsDataTable from 'components/JournalEntry/ManualJournalsDataTable'; +import DashboardActionsBar from 'components/JournalEntry/ManualJournalActionsBar'; +import ManualJournalsConnect from 'connectors/ManualJournals.connect'; +import DashboardConnect from 'connectors/Dashboard.connector'; +import CustomViewConnect from 'connectors/CustomView.connector'; +import ResourceConnect from 'connectors/Resource.connector'; +import { compose } from 'utils'; + +function ManualJournalsTable({ + changePageTitle, + fetchResourceViews, + fetchManualJournalsTable, + requestDeleteManualJournal, + requestPublishManualJournal, +}) { + const history = useHistory(); + const [deleteManualJournal, setDeleteManualJournal] = useState(false); + + const fetchHook = useAsync(async () => { + await Promise.all([ + fetchResourceViews('manual_journals'), + ]); + }); + + const fetchManualJournalsHook = useAsync(async () => { + return fetchManualJournalsTable(); + }, false); + + useEffect(() => { + changePageTitle('Manual Journals'); + }, []); + + const handleCancelManualJournalDelete = () => { + setDeleteManualJournal(false); + }; + + const handleConfirmManualJournalDelete = useCallback(() => { + requestDeleteManualJournal(deleteManualJournal.id).then(() => { + setDeleteManualJournal(false); + AppToaster.show({ message: 'the_manual_Journal_has_been_deleted' }); + }); + }, [deleteManualJournal, requestDeleteManualJournal]); + + const handleEditJournal = useCallback((journal) => { + history.push(`/dashboard/accounting/manual-journals/${journal.id}/edit`); + }, [history]); + + const handleDeleteJournal = useCallback((journal) => { + setDeleteManualJournal(journal); + }, []); + + const handleFilterChanged = useCallback(() => { + fetchManualJournalsHook.execute(); + }, []); + + const handleViewChanged = useCallback(() => { + fetchManualJournalsHook.execute(); + }, []); + + const handleFetchData = useCallback(() => { + fetchManualJournalsHook.execute(); + }, []); + + const handlePublishJournal = useCallback((journal) => { + requestPublishManualJournal(journal.id).then(() => { + AppToaster.show({ message: 'the_manual_journal_id_has_been_published' }); + }) + }, [requestPublishManualJournal]); + + return ( + + + + + + + + + + + + + +

+ Are you sure you want to move filename to Trash? You will be + able to restore it later, but it will become private to you. +

+
+
+
+ ); +} + +export default compose( + ManualJournalsConnect, + CustomViewConnect, + ResourceConnect, + DashboardConnect +)(ManualJournalsTable); diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js index e97152b4f..a5cdfd673 100644 --- a/client/src/routes/dashboard.js +++ b/client/src/routes/dashboard.js @@ -67,7 +67,7 @@ export default [ }, { - path: `${BASE_URL}/accounting/manual-journal/:id`, + path: `${BASE_URL}/accounting/manual-journals/:id/edit`, name: 'dashboard.manual.journal.edit', component: LazyLoader({ loader: () => @@ -75,6 +75,15 @@ export default [ }), }, + { + path: `${BASE_URL}/accounting/manual-journals`, + component: LazyLoader({ + loader: () => + import('containers/Dashboard/Accounting/ManualJournalsTable') + }), + text: 'Manual Journals' + }, + // Items { path: `${BASE_URL}/items/list`, @@ -95,14 +104,16 @@ export default [ loader: () => import('containers/Dashboard/Items/ItemsCategoryList') }) }, - , + // Financial Reports. { path: `${BASE_URL}/accounting/general-ledger`, name: 'dashboard.accounting.general.ledger', component: LazyLoader({ loader: () => - import('containers/Dashboard/FinancialStatements/GeneralLedger/GeneralLedger') + import( + 'containers/Dashboard/FinancialStatements/GeneralLedger/GeneralLedger' + ) }) }, { diff --git a/client/src/static/json/icons.js b/client/src/static/json/icons.js index bbc23219f..fd09923db 100644 --- a/client/src/static/json/icons.js +++ b/client/src/static/json/icons.js @@ -98,5 +98,9 @@ export default { "times-circle": { path: ['M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 464c-118.7 0-216-96.1-216-216 0-118.7 96.1-216 216-216 118.7 0 216 96.1 216 216 0 118.7-96.1 216-216 216zm94.8-285.3L281.5 256l69.3 69.3c4.7 4.7 4.7 12.3 0 17l-8.5 8.5c-4.7 4.7-12.3 4.7-17 0L256 281.5l-69.3 69.3c-4.7 4.7-12.3 4.7-17 0l-8.5-8.5c-4.7-4.7-4.7-12.3 0-17l69.3-69.3-69.3-69.3c-4.7-4.7-4.7-12.3 0-17l8.5-8.5c4.7-4.7 12.3-4.7 17 0l69.3 69.3 69.3-69.3c4.7-4.7 12.3-4.7 17 0l8.5 8.5c4.6 4.7 4.6 12.3 0 17z'], viewBox: '0 0 512 512', - } + }, + "file-alt": { + path: ['M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zm-22.6 22.7c2.1 2.1 3.5 4.6 4.2 7.4H256V32.5c2.8.7 5.3 2.1 7.4 4.2l83.9 83.9zM336 480H48c-8.8 0-16-7.2-16-16V48c0-8.8 7.2-16 16-16h176v104c0 13.3 10.7 24 24 24h104v304c0 8.8-7.2 16-16 16zm-48-244v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm0 64v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm0 64v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12z'], + viewBox: '0 0 384 512' + }, } \ No newline at end of file diff --git a/client/src/store/accounting/accounting.actions.js b/client/src/store/accounting/accounting.actions.js deleted file mode 100644 index a2be95e35..000000000 --- a/client/src/store/accounting/accounting.actions.js +++ /dev/null @@ -1,33 +0,0 @@ -import ApiService from 'services/ApiService'; -import t from 'store/types'; - -export const makeJournalEntries = ({ form }) => { - return (dispatch) => new Promise((resolve, reject) => { - ApiService.post('accounting/make-journal-entries', form).then((response) => { - resolve(response); - }).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); }); - }); -} \ No newline at end of file diff --git a/client/src/store/accounting/accounting.reducers.js b/client/src/store/accounting/accounting.reducers.js deleted file mode 100644 index 8d47a87cb..000000000 --- a/client/src/store/accounting/accounting.reducers.js +++ /dev/null @@ -1,19 +0,0 @@ -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]; -} \ No newline at end of file diff --git a/client/src/store/accounting/accounting.types.js b/client/src/store/accounting/accounting.types.js deleted file mode 100644 index 84eae0701..000000000 --- a/client/src/store/accounting/accounting.types.js +++ /dev/null @@ -1,6 +0,0 @@ - - -export default { - MAKE_JOURNAL_ENTRIES: 'MAKE_JOURNAL_ENTRIES', - MANUAL_JOURNAL_SET: 'MANUAL_JOURNAL_SET', -} \ No newline at end of file diff --git a/client/src/store/accounts/accounts.selectors.js b/client/src/store/accounts/accounts.selectors.js index 56ff4c07f..6192243a2 100644 --- a/client/src/store/accounts/accounts.selectors.js +++ b/client/src/store/accounts/accounts.selectors.js @@ -1,9 +1,11 @@ -import {pickItemsFromIds} from 'store/selectors'; +import { pickItemsFromIds } from 'store/selectors'; export const getAccountsItems = (state, viewId) => { - const accountsView = state.accounts.views[(viewId || -1)]; + + const accountsView = state.accounts.views[viewId || -1]; const accountsItems = state.accounts.items; - return (typeof accountsView === 'object') - ? (pickItemsFromIds(accountsItems, accountsView.ids) || []) : []; -} \ No newline at end of file + return typeof accountsView === 'object' + ? pickItemsFromIds(accountsItems, accountsView.ids) || [] + : []; +}; diff --git a/client/src/store/manualJournals/manualJournals.actions.js b/client/src/store/manualJournals/manualJournals.actions.js new file mode 100644 index 000000000..4eeb4749c --- /dev/null +++ b/client/src/store/manualJournals/manualJournals.actions.js @@ -0,0 +1,97 @@ +import ApiService from 'services/ApiService'; +import t from 'store/types'; + +export const makeJournalEntries = ({ form }) => { + return (dispatch) => new Promise((resolve, reject) => { + ApiService.post('accounting/make-journal-entries', form).then((response) => { + resolve(response); + }).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); }); + }); +}; + +export const deleteManualJournal = ({ id }) => { + return (dispatch) => + new Promise((resolve, reject) => { + ApiService.delete(`accounting/manual-journals/${id}`) + .then((response) => { + dispatch({ + type: t.MANUAL_JOURNAL_REMOVE, + payload: { id }, + }); + resolve(response); + }) + .catch((error) => { reject(error); }); + }); +}; + +export const publishManualJournal = ({ id }) => { + return (dispatch) => + new Promise((resolve, reject) => { + ApiService.post(`accounting/manual-journals/${id}/publish`) + .then((response) => { + dispatch({ + type: t.MANUAL_JOURNAL_PUBLISH, + payload: { id }, + }); + resolve(response); + }) + .catch((error) => { reject(error); }); + }); +} + +export const fetchManualJournalsTable = ({ query } = {}) => { + return (dispatch, getState) => + new Promise((resolve, reject) => { + const pageQuery = getState().manualJournals.tableQuery; + dispatch({ + type: t.MANUAL_JOURNALS_TABLE_LOADING, + loading: true, + }); + ApiService.get('accounting/manual-journals', { + params: { ...pageQuery, ...query }, + }) + .then((response) => { + + dispatch({ + type: t.MANUAL_JOURNALS_PAGE_SET, + manual_journals: response.data.manualJournals, + customViewId: response.data.customViewId, + }); + dispatch({ + type: t.MANUAL_JOURNALS_ITEMS_SET, + manual_journals: response.data.manualJournals, + }); + dispatch({ + type: t.MANUAL_JOURNALS_TABLE_LOADING, + loading: false, + }); + resolve(response); + }) + .catch((error) => { + reject(error); + }); + }); +}; diff --git a/client/src/store/manualJournals/manualJournals.reducers.js b/client/src/store/manualJournals/manualJournals.reducers.js new file mode 100644 index 000000000..25c94c6be --- /dev/null +++ b/client/src/store/manualJournals/manualJournals.reducers.js @@ -0,0 +1,68 @@ +import t from 'store/types'; +import { createReducer } from '@reduxjs/toolkit'; +import { omit } from 'lodash'; + +const initialState = { + items: {}, + views: {}, + loading: false, + currentViewId: -1, + tableQuery: {}, +}; + +export default createReducer(initialState, { + + [t.MANUAL_JOURNAL_SET]: (state, action) => { + const { id, manualJournal } = action.payload; + state.items[id] = manualJournal; + }, + + [t.MANUAL_JOURNAL_PUBLISH]: (state, action) => { + const { id } = action.payload; + const item = state.items[id] || {}; + + state.items[id] = { + ...item, status: 1, + }; + }, + + [t.MANUAL_JOURNALS_ITEMS_SET]: (state, action) => { + const _manual_journals = {}; + + action.manual_journals.forEach((manual_journal) => { + _manual_journals[manual_journal.id] = manual_journal; + }); + state.items = { + ...state.items, + ..._manual_journals, + }; + }, + + [t.MANUAL_JOURNALS_PAGE_SET]: (state, action) => { + const viewId = action.customViewId || -1; + const view = state.views[viewId] || {}; + + state.views[viewId] = { + ...view, + ids: action.manual_journals.map((i) => i.id), + }; + }, + + [t.MANUAL_JOURNALS_TABLE_LOADING]: (state, action) => { + state.loading = action.loading; + }, + + [t.MANUAL_JOURNALS_SET_CURRENT_VIEW]: (state, action) => { + state.currentViewId = action.currentViewId; + }, + + [t.MANUAL_JOURNAL_REMOVE]: (state, action) => { + const { id } = action.payload; + state.items = omit(state.items, [id]); + } +}); + + +export const getManualJournal = (state, id) => { + return state.manualJournals.items[id]; +} \ No newline at end of file diff --git a/client/src/store/manualJournals/manualJournals.selectors.js b/client/src/store/manualJournals/manualJournals.selectors.js new file mode 100644 index 000000000..d8dc48789 --- /dev/null +++ b/client/src/store/manualJournals/manualJournals.selectors.js @@ -0,0 +1,10 @@ +import { pickItemsFromIds } from 'store/selectors'; + +export const getManualJournalsItems = (state, viewId) => { + const accountsView = state.manualJournals.views[viewId || -1]; + const accountsItems = state.manualJournals.items; + + return typeof accountsView === 'object' + ? pickItemsFromIds(accountsItems, accountsView.ids) || [] + : []; +}; diff --git a/client/src/store/manualJournals/manualJournals.types.js b/client/src/store/manualJournals/manualJournals.types.js new file mode 100644 index 000000000..1bff8414d --- /dev/null +++ b/client/src/store/manualJournals/manualJournals.types.js @@ -0,0 +1,13 @@ +export default { + MAKE_JOURNAL_ENTRIES: 'MAKE_JOURNAL_ENTRIES', + MANUAL_JOURNAL_SET: 'MANUAL_JOURNAL_SET', + + MANUAL_JOURNALS_TABLE_LOADING: 'MANUAL_JOURNALS_TABLE_LOADING', + MANUAL_JOURNALS_PAGE_SET: 'MANUAL_JOURNALS_PAGE_SET', + MANUAL_JOURNALS_ITEMS_SET: 'MANUAL_JOURNALS_ITEMS_SET', + MANUAL_JOURNALS_SET_CURRENT_VIEW: 'MANUAL_JOURNALS_SET_CURRENT_VIEW', + MANUAL_JOURNALS_TABLE_QUERIES_ADD: 'MANUAL_JOURNALS_TABLE_QUERIES_ADD', + MANUAL_JOURNAL_REMOVE: 'MANUAL_JOURNAL_REMOVE', + + MANUAL_JOURNAL_PUBLISH: 'MANUAL_JOURNAL_PUBLISH', +}; diff --git a/client/src/store/reducers.js b/client/src/store/reducers.js index e050e2d9f..a41666779 100644 --- a/client/src/store/reducers.js +++ b/client/src/store/reducers.js @@ -11,16 +11,16 @@ import expenses from './expenses/expenses.reducer'; import currencies from './currencies/currencies.reducer'; import resources from './resources/resources.reducer'; import financialStatements from './financialStatement/financialStatements.reducer'; -import itemCategories from './itemCategories/itemsCateory.reducer'; +import itemCategories from './itemCategories/itemsCategory.reducer'; import settings from './settings/settings.reducer'; -import accounting from './accounting/accounting.reducers'; +import manualJournals from './manualJournals/manualJournals.reducers'; export default combineReducers({ authentication, dashboard, users, accounts, - accounting, + manualJournals, fields, views, expenses, diff --git a/client/src/store/types.js b/client/src/store/types.js index 29adce107..a157d63db 100644 --- a/client/src/store/types.js +++ b/client/src/store/types.js @@ -1,6 +1,6 @@ import authentication from './authentication/authentication.types'; import accounts from './accounts/accounts.types'; -import accounting from './accounting/accounting.types' +import accounting from './manualJournals/manualJournals.types' import currencies from './currencies/currencies.types'; import customFields from './customFields/customFields.types'; import customViews from './customViews/customViews.types'; diff --git a/client/src/style/App.scss b/client/src/style/App.scss index 5ebc46caf..5d0c45e8d 100644 --- a/client/src/style/App.scss +++ b/client/src/style/App.scss @@ -35,6 +35,7 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, @import "pages/make-journal-entries"; @import "pages/preferences"; @import "pages/view-form"; +@import "pages/manual-journals"; // Views @import "views/filter-dropdown"; diff --git a/client/src/style/components/data-table.scss b/client/src/style/components/data-table.scss index f607ab572..f06e908c0 100644 --- a/client/src/style/components/data-table.scss +++ b/client/src/style/components/data-table.scss @@ -16,7 +16,7 @@ overflow-x: hidden; .th{ - padding: 1rem 1.5rem; + padding: 1rem 0.5rem; background: #F8FAFA; font-size: 14px; color: #666; @@ -54,7 +54,6 @@ .bp3-control{ margin-bottom: 0; } - .resizer { display: inline-block; background: transparent; @@ -90,8 +89,8 @@ .tr .td{ border-bottom: 1px solid #E0E2E2; - } - + align-items: center; + } .td.actions .#{$ns}-button{ background: #E6EFFB; border: 0; diff --git a/client/src/style/pages/manual-journals.scss b/client/src/style/pages/manual-journals.scss new file mode 100644 index 000000000..5e130f152 --- /dev/null +++ b/client/src/style/pages/manual-journals.scss @@ -0,0 +1,26 @@ + + +.dashboard__insider--manual-journals{ + + .bigcapital-datatable{ + + .thead{ + + } + + .tbody{ + .amount > span{ + font-weight: 600; + } + .note{ + .bp3-icon{ + color: #666; + } + } + .status{ + font-size: 13px; + text-transform: uppercase; + } + } + } +} \ No newline at end of file diff --git a/server/src/http/controllers/Accounting.js b/server/src/http/controllers/Accounting.js index dc7165a69..78d59150e 100644 --- a/server/src/http/controllers/Accounting.js +++ b/server/src/http/controllers/Accounting.js @@ -433,11 +433,15 @@ export default { errors: [{ type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100 }], }); } - if (!manualJournal.status) { + if (manualJournal.status) { return res.status(400).send({ errors: [{ type: 'MANUAL.JOURNAL.PUBLISHED.ALREADY', code: 200 }], }); } + const updateJournalTransactionOper = ManualJournal.query() + .where('id', manualJournal.id) + .update({ status: 1 }); + const transactions = await AccountTransaction.query() .whereIn('reference_type', ['Journal', 'ManualJournal']) .where('reference_id', manualJournal.id) @@ -452,6 +456,7 @@ export default { .update({ draft: 0 }); await Promise.all([ + updateJournalTransactionOper, updateAccountsTransactionsOper, journal.saveBalance(), ]);