diff --git a/client/src/components/Dashboard/Dashboard.js b/client/src/components/Dashboard/Dashboard.js
index 6ead8488c..c00bb07d4 100644
--- a/client/src/components/Dashboard/Dashboard.js
+++ b/client/src/components/Dashboard/Dashboard.js
@@ -2,6 +2,8 @@ import React from 'react';
import { Switch, Route } from 'react-router';
import { useQuery } from 'react-query';
+import 'style/pages/Dashboard/Dashboard.scss';
+
import DashboardLoadingIndicator from './DashboardLoadingIndicator';
import Sidebar from 'components/Sidebar/Sidebar';
@@ -15,8 +17,6 @@ import withSettingsActions from 'containers/Settings/withSettingsActions';
import { compose } from 'utils';
-import 'style/pages/Dashboard/Dashboard.scss';
-
/**
* Dashboard page.
*/
diff --git a/client/src/components/DataTable.js b/client/src/components/DataTable.js
index 6dd95ff0a..f808755d9 100644
--- a/client/src/components/DataTable.js
+++ b/client/src/components/DataTable.js
@@ -191,7 +191,7 @@ export default function DataTable({
// Renders table cell.
const RenderCell = useCallback(
- ({ row, cell, index }) => (
+ ({ row, cell, column, index }) => (
(
@@ -199,6 +199,7 @@ export default function DataTable({
style={{
'padding-left': `${row.depth * expandColumnSpace}rem`,
}}
+ className={'expend-padding'}
>
{children}
@@ -224,7 +225,14 @@ export default function DataTable({
/>
- {cell.render('Cell')}
+
+ (
+ { children }
+ )}>
+ {cell.render('Cell')}
+
),
[expandable, expandToggleColumn, expandColumnSpace],
@@ -276,7 +284,13 @@ export default function DataTable({
return (
diff --git a/client/src/components/Datatable/Cells.js b/client/src/components/Datatable/Cells.js
new file mode 100644
index 000000000..8c5efd370
--- /dev/null
+++ b/client/src/components/Datatable/Cells.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export function CellTextSpan({ cell: { value } }) {
+ return (
{ value })
+}
diff --git a/client/src/components/FinancialSheet.js b/client/src/components/FinancialSheet.js
index a46eafdf6..181747f35 100644
--- a/client/src/components/FinancialSheet.js
+++ b/client/src/components/FinancialSheet.js
@@ -3,9 +3,10 @@ import moment from 'moment';
import classnames from 'classnames';
import { FormattedMessage as T, useIntl } from 'react-intl';
+import 'style/pages/FinancialStatements/FinancialSheet.scss';
+
import { If, LoadingIndicator, MODIFIER } from 'components';
-import 'style/pages/FinancialStatements/FinancialSheet.scss';
export default function FinancialSheet({
companyName,
@@ -20,7 +21,8 @@ export default function FinancialSheet({
className,
basis,
minimal = false,
- fullWidth = false
+ fullWidth = false,
+ currentDate = true,
}) {
const { formatMessage } = useIntl();
const format = 'DD MMMM YYYY';
@@ -84,11 +86,19 @@ export default function FinancialSheet({
{children}
{accountingBasis}
- {basisLabel && (
-
- {basisLabel}
-
- )}
+
);
diff --git a/client/src/containers/Accounts/AccountsDataTable.js b/client/src/containers/Accounts/AccountsDataTable.js
index 27ee94cc0..14ab2c163 100644
--- a/client/src/containers/Accounts/AccountsDataTable.js
+++ b/client/src/containers/Accounts/AccountsDataTable.js
@@ -27,8 +27,6 @@ import withAccounts from 'containers/Accounts/withAccounts';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withCurrentView from 'containers/Views/withCurrentView';
-import { accountNameAccessor } from './utils';
-
function AccountsDataTable({
// #withDashboardActions
accountsTable,
@@ -136,7 +134,7 @@ function AccountsDataTable({
{
id: 'name',
Header: formatMessage({ id: 'account_name' }),
- accessor: accountNameAccessor,
+ accessor: 'name',
className: 'account_name',
width: 220,
},
@@ -145,7 +143,7 @@ function AccountsDataTable({
Header: formatMessage({ id: 'code' }),
accessor: 'code',
className: 'code',
- width: 125,
+ width: 70,
},
{
id: 'type',
@@ -160,13 +158,13 @@ function AccountsDataTable({
Cell: NormalCell,
accessor: 'type.normal',
className: 'normal',
- width: 115,
+ width: 65,
},
{
id: 'currency',
Header: formatMessage({ id: 'currency' }),
accessor: (row) => 'USD',
- width: 100,
+ width: 75,
},
{
id: 'balance',
diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.js
index d3d74a593..88dc2f23f 100644
--- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.js
+++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryTable.js
@@ -38,6 +38,7 @@ function ReceivableAgingSummaryTable({
className: 'customer_name',
sticky: 'left',
width: 240,
+ textOverview: true,
},
{
Header: ,
diff --git a/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheetTable.js b/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheetTable.js
index d1d14fac8..c3c2044d5 100644
--- a/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheetTable.js
+++ b/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheetTable.js
@@ -4,33 +4,12 @@ import classNames from 'classnames';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
+import { CellTextSpan } from 'components/Datatable/Cells';
import withBalanceSheetDetail from './withBalanceSheetDetail';
import { compose, defaultExpanderReducer, getColumnWidth } from 'utils';
-// Total cell.
-function TotalCell({ cell }) {
- const row = cell.row.original;
-
- if (row.total) {
- return row.total.formatted_amount;
- }
- return '';
-}
-
-// Total period cell.
-const TotalPeriodCell = (index) => ({ cell }) => {
- const { original } = cell.row;
-
- if (original.total_periods && original.total_periods[index]) {
- const amount = original.total_periods[index].formatted_amount;
-
- return amount;
- }
- return '';
-};
-
/**
* Balance sheet table.
*/
@@ -52,14 +31,15 @@ function BalanceSheetTable({
Header: formatMessage({ id: 'account_name' }),
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
className: 'account_name',
+ textOverview: true,
width: 240,
},
...(balanceSheetQuery.display_columns_type === 'total'
? [
{
Header: formatMessage({ id: 'total' }),
- accessor: 'balance.formatted_amount',
- Cell: TotalCell,
+ accessor: 'total.formatted_amount',
+ Cell: CellTextSpan,
className: 'total',
width: 140,
},
@@ -69,8 +49,8 @@ function BalanceSheetTable({
? balanceSheetColumns.map((column, index) => ({
id: `date_period_${index}`,
Header: column,
- accessor: `total_periods[${index}]`,
- Cell: TotalPeriodCell(index),
+ Cell: CellTextSpan,
+ accessor: `total_periods[${index}].formatted_amount`,
className: classNames('total-period', `total-periods-${index}`),
width: getColumnWidth(
balanceSheetTableRows,
@@ -93,7 +73,7 @@ function BalanceSheetTable({
const { original } = row;
const rowTypes = Array.isArray(original.row_types)
? original.row_types
- : [];
+ : [original.row_types];
return {
...rowTypes.reduce((acc, rowType) => {
diff --git a/client/src/containers/FinancialStatements/FinancialAccountsFilter.js b/client/src/containers/FinancialStatements/FinancialAccountsFilter.js
index 233865ad0..68c6da790 100644
--- a/client/src/containers/FinancialStatements/FinancialAccountsFilter.js
+++ b/client/src/containers/FinancialStatements/FinancialAccountsFilter.js
@@ -14,6 +14,8 @@ import { CLASSES } from 'common/classes';
import { Col, Row, ListSelect, MODIFIER } from 'components';
import { filterAccountsOptions } from './common';
+
+
export default function FinancialAccountsFilter({ ...restProps }) {
const SUBMENU_POPOVER_MODIFIERS = {
flip: { boundariesElement: 'viewport', padding: 20 },
diff --git a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js
index b36a53803..927582c62 100644
--- a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js
+++ b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js
@@ -49,6 +49,7 @@ function GeneralLedger({
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural',
+ accountsFilter: 'with-transactions',
});
// Change page title of the dashboard.
diff --git a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedgerHeaderGeneralPane.js b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedgerHeaderGeneralPane.js
index b9d3cccc8..7f7f455d1 100644
--- a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedgerHeaderGeneralPane.js
+++ b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedgerHeaderGeneralPane.js
@@ -1,16 +1,21 @@
import React from 'react';
-import { FormGroup, Classes } from '@blueprintjs/core';
+import {
+ FormGroup,
+ Classes,
+} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
+import { compose } from 'redux';
import { AccountsMultiSelect, Row, Col } from 'components';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import RadiosAccountingBasis from '../RadiosAccountingBasis';
+import FinancialAccountsFilter from '../FinancialAccountsFilter';
import withAccounts from 'containers/Accounts/withAccounts';
-import { compose } from 'redux';
+import { filterAccountsOptions } from './common';
/**
* General ledger (GL) - Header - General panel.
@@ -22,7 +27,10 @@ function GeneralLedgerHeaderGeneralPane({
return (
-
+
{
- switch (row.rowType) {
- case ROW_TYPE.OPENING_BALANCE:
- return 'Opening Balance';
- case ROW_TYPE.CLOSING_BALANCE:
- return 'Closing Balance';
- default:
- return row.name;
- }
- };
-
- // Date accessor.
- const dateAccessor = (row) => {
- const TYPES = [
- ROW_TYPE.OPENING_BALANCE,
- ROW_TYPE.CLOSING_BALANCE,
- ROW_TYPE.TRANSACTION,
- ];
-
- return TYPES.indexOf(row.rowType) !== -1
- ? moment(row.date).format('DD MMM YYYY')
- : '';
- };
-
- // Amount cell
- const amountCell = useCallback(({ cell }) => {
- const transaction = cell.row.original;
-
- if (transaction.rowType === ROW_TYPE.ACCOUNT) {
- return !cell.row.isExpanded ? (
-
- ) : (
- ''
- );
- }
- return ;
- }, []);
-
const columns = useMemo(
() => [
{
- Header: formatMessage({ id: 'account_name' }),
- accessor: accountNameAccessor,
- className: 'name',
- width: 225,
+ Header: formatMessage({ id: 'date' }),
+ accessor: (row) => {
+ if (row.rowType === 'ACCOUNT_ROW') {
+ return (
+
+ {row.date}
+
+ );
+ }
+ return row.date;
+ },
+ className: 'date',
+ width: 120,
},
{
- Header: formatMessage({ id: 'date' }),
- accessor: dateAccessor,
- className: 'date',
- width: 115,
+ Header: formatMessage({ id: 'account_name' }),
+ accessor: 'name',
+ className: 'name',
+ textOverview: true,
+ // width: 200,
},
{
Header: formatMessage({ id: 'transaction_type' }),
- accessor: 'referenceType',
+ accessor: 'reference_type_formatted',
className: 'transaction_type',
- width: 145,
+ width: 125 ,
},
{
- Header: formatMessage({ id: 'trans_num' }),
+ Header: formatMessage({ id: 'transaction_number' }),
accessor: 'reference_id',
className: 'transaction_number',
- width: 110,
+ width: 100,
},
{
Header: formatMessage({ id: 'description' }),
accessor: 'note',
className: 'description',
- width: 145,
+ // width: 145,
+ },
+ {
+ Header: formatMessage({ id: 'credit' }),
+ accessor: 'formatted_credit',
+ className: 'credit',
+ width: getColumnWidth(generalLedgerTableRows, 'formatted_credit', {
+ minWidth: 100,
+ magicSpacing: 10,
+ }),
+ },
+ {
+ Header: formatMessage({ id: 'debit' }),
+ accessor: 'formatted_debit',
+ className: 'debit',
+ width: getColumnWidth(generalLedgerTableRows, 'formatted_debit', {
+ minWidth: 100,
+ magicSpacing: 10,
+ }),
},
{
Header: formatMessage({ id: 'amount' }),
- Cell: amountCell,
+ accessor: 'formatted_amount',
className: 'amount',
- width: 150,
+ width: getColumnWidth(generalLedgerTableRows, 'formatted_amount', {
+ minWidth: 100,
+ magicSpacing: 10,
+ }),
},
{
- Header: formatMessage({ id: 'balance' }),
- Cell: amountCell,
- className: 'balance',
- width: 150,
+ Header: formatMessage({ id: 'running_balance' }),
+ accessor: 'formatted_running_balance',
+ className: 'running_balance',
+ width: getColumnWidth(generalLedgerTableRows, 'formatted_running_balance', {
+ minWidth: 100,
+ magicSpacing: 10,
+ }),
},
],
- [],
+ [formatMessage, generalLedgerTableRows],
);
// Default expanded rows of general ledger table.
@@ -140,7 +139,7 @@ function GeneralLedgerTable({
rowClassNames={rowClassNames}
expanded={expandedRows}
virtualizedRows={true}
- fixedItemSize={37}
+ fixedItemSize={30}
fixedSizeHeight={1000}
expandable={true}
expandToggleColumn={1}
diff --git a/client/src/containers/FinancialStatements/GeneralLedger/common.js b/client/src/containers/FinancialStatements/GeneralLedger/common.js
new file mode 100644
index 000000000..76c57d092
--- /dev/null
+++ b/client/src/containers/FinancialStatements/GeneralLedger/common.js
@@ -0,0 +1,16 @@
+import { formatMessage } from 'services/intl';
+
+export const filterAccountsOptions = [
+ {
+ key: 'all-accounts',
+ name: formatMessage({ id: 'all_accounts' }),
+ hint: formatMessage({ id: 'all_accounts_including_with_zero_balance' }),
+ },
+ {
+ key: 'with-transactions',
+ name: formatMessage({ id: 'accounts_with_transactions' }),
+ hint: formatMessage({
+ id: 'include_accounts_once_has_transactions_on_given_date_period',
+ }),
+ },
+];
diff --git a/client/src/containers/FinancialStatements/Journal/JournalTable.js b/client/src/containers/FinancialStatements/Journal/JournalTable.js
index 93779678f..0c36b2661 100644
--- a/client/src/containers/FinancialStatements/Journal/JournalTable.js
+++ b/client/src/containers/FinancialStatements/Journal/JournalTable.js
@@ -8,7 +8,7 @@ import Money from 'components/Money';
import withJournal from './withJournal';
-import { compose, defaultExpanderReducer } from 'utils';
+import { compose, defaultExpanderReducer, getForceWidth } from 'utils';
function JournalSheetTable({
// #withJournal
@@ -22,70 +22,52 @@ function JournalSheetTable({
}) {
const { formatMessage } = useIntl();
- const rowTypeFilter = (rowType, value, types) => {
- return types.indexOf(rowType) === -1 ? '' : value;
- };
-
- const exceptRowTypes = (rowType, value, types) => {
- return types.indexOf(rowType) !== -1 ? '' : value;
- };
-
const columns = useMemo(
() => [
{
Header: formatMessage({ id: 'date' }),
- accessor: (r) =>
- rowTypeFilter(r.rowType, moment(r.date).format('YYYY MMM DD'), [
- 'first_entry',
- ]),
+ accessor: row => row.date ? moment(row.date).format('YYYY MMM DD') : '',
className: 'date',
- width: 85,
+ width: 100,
},
{
Header: formatMessage({ id: 'transaction_type' }),
- accessor: (r) =>
- rowTypeFilter(r.rowType, r.transaction_type, ['first_entry']),
+ accessor: 'reference_type_formatted',
className: 'reference_type_formatted',
- width: 145,
+ width: 120,
},
{
Header: formatMessage({ id: 'num' }),
- accessor: (r) =>
- rowTypeFilter(r.rowType, r.reference_id, ['first_entry']),
+ accessor: 'reference_id',
className: 'reference_id',
width: 70,
},
{
Header: formatMessage({ id: 'description' }),
accessor: 'note',
+ className: 'note'
},
{
Header: formatMessage({ id: 'acc_code' }),
- accessor: 'account.code',
+ accessor: 'account_code',
width: 95,
className: 'account_code',
},
{
Header: formatMessage({ id: 'account' }),
- accessor: 'account.name',
+ accessor: 'account_name',
+ className: 'account_name',
+ textOverview: true,
},
{
Header: formatMessage({ id: 'credit' }),
- accessor: (r) =>
- exceptRowTypes(
- r.rowType,
- ,
- ['space_entry'],
- ),
+ accessor: 'formatted_credit',
+ className: 'credit'
},
{
Header: formatMessage({ id: 'debit' }),
- accessor: (r) =>
- exceptRowTypes(
- r.rowType,
- ,
- ['space_entry'],
- ),
+ accessor: 'formatted_debit',
+ className: 'debit'
},
],
[formatMessage],
@@ -101,6 +83,20 @@ function JournalSheetTable({
// Default expanded rows of general journal table.
const expandedRows = useMemo(() => defaultExpanderReducer([], 1), []);
+ const rowClassNames = useCallback((row) => {
+ const { original } = row;
+ const rowTypes = Array.isArray(original.rowType)
+ ? original.rowType
+ : [original.rowType];
+
+ return {
+ ...rowTypes.reduce((acc, rowType) => {
+ acc[`row_type--${rowType}`] = rowType;
+ return acc;
+ }, {}),
+ };
+ }, []);
+
return (
+ >
(row.code ? `${row.name} - ${row.code}` : row.name),
className: 'name',
+ textOverview: true,
width: 240,
},
...(profitLossQuery.display_columns_type === 'total'
? [
{
Header: formatMessage({ id: 'total' }),
+ Cell: CellTextSpan,
accessor: 'total.formatted_amount',
className: 'total',
width: 140,
@@ -42,6 +44,7 @@ function ProfitLossSheetTable({
? profitLossColumns.map((column, index) => ({
id: `date_period_${index}`,
Header: column,
+ Cell: CellTextSpan,
accessor: `total_periods[${index}].formatted_amount`,
width: getColumnWidth(
profitLossTableRows,
diff --git a/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheetTable.js b/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheetTable.js
index cf60323a0..73395aac3 100644
--- a/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheetTable.js
+++ b/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheetTable.js
@@ -3,7 +3,7 @@ import { useIntl } from 'react-intl';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
-import Money from 'components/Money';
+import { CellTextSpan } from 'components/Datatable/Cells';
import withTrialBalance from './withTrialBalance';
@@ -28,9 +28,11 @@ function TrialBalanceSheetTable({
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
className: 'name',
width: 160,
+ textOverview: true,
},
{
Header: formatMessage({ id: 'credit' }),
+ Cell: CellTextSpan,
accessor: 'formatted_credit',
className: 'credit',
width: getColumnWidth(trialBalanceTableRows, `credit`, {
@@ -39,11 +41,13 @@ function TrialBalanceSheetTable({
},
{
Header: formatMessage({ id: 'debit' }),
+ Cell: CellTextSpan,
accessor: 'formatted_debit',
width: getColumnWidth(trialBalanceTableRows, `debit`, { minWidth: 95 }),
},
{
Header: formatMessage({ id: 'balance' }),
+ Cell: CellTextSpan,
accessor: 'formatted_balance',
className: 'balance',
width: getColumnWidth(trialBalanceTableRows, `balance`, {
@@ -56,7 +60,7 @@ function TrialBalanceSheetTable({
const rowClassNames = (row) => {
const { original } = row;
- const rowTypes = Array.isArray(original.rowTypes) ? original.rowTypes : [];
+ const rowTypes = Array.isArray(original.rowType) ? original.rowType : [original.rowType];
return {
...rowTypes.reduce((acc, rowType) => {
diff --git a/client/src/containers/FinancialStatements/common.js b/client/src/containers/FinancialStatements/common.js
index a4ea8fc64..216e06ab4 100644
--- a/client/src/containers/FinancialStatements/common.js
+++ b/client/src/containers/FinancialStatements/common.js
@@ -1,4 +1,4 @@
-import { mapKeys, omit, snakeCase } from 'lodash';
+import { omit } from 'lodash';
import { transformToCamelCase, flatObject } from 'utils';
import { formatMessage } from 'services/intl';
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index a04766a30..6601aba16 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -974,4 +974,6 @@ export default {
specific_customers: 'Specific Customers',
all_customers: 'All Customers',
selected_customers: '{count} Selected Customers',
+ transaction_number: 'Transaction #',
+ running_balance: 'Running balance'
};
diff --git a/client/src/store/financialStatement/financialStatements.mappers.js b/client/src/store/financialStatement/financialStatements.mappers.js
index dd8d4e419..bc21867f5 100644
--- a/client/src/store/financialStatement/financialStatements.mappers.js
+++ b/client/src/store/financialStatement/financialStatements.mappers.js
@@ -1,14 +1,8 @@
-import { omit } from 'lodash';
+import { omit, chain } from 'lodash';
+import moment from 'moment';
export const mapBalanceSheetToTableRows = (accounts) => {
return accounts.map((account) => {
- const PRIMARY_SECTIONS = ['assets', 'liability', 'equity'];
- const rowTypes = [
- 'total_row',
- ...(PRIMARY_SECTIONS.indexOf(account.section_type) !== -1
- ? ['total_assets']
- : []),
- ];
return {
...account,
children: mapBalanceSheetToTableRows([
@@ -31,51 +25,79 @@ export const mapBalanceSheetToTableRows = (accounts) => {
};
export const journalToTableRowsMapper = (journal) => {
- return journal.reduce((rows, journal) => {
- journal.entries.forEach((entry, index) => {
- rows.push({
- ...entry,
- rowType: index === 0 ? 'first_entry' : 'entry',
- });
- });
- rows.push({
- credit: journal.credit,
- debit: journal.debit,
- rowType: 'entries_total',
- });
- rows.push({
- rowType: 'space_entry',
- });
- return rows;
- }, []);
+ const TYPES = {
+ ENTRY: 'ENTRY',
+ TOTAL_ENTRIES: 'TOTAL_ENTRIES',
+ EMPTY_ROW: 'EMPTY_ROW',
+ };
+
+ const entriesMapper = (transaction) => {
+ return transaction.entries.map((entry, index) => ({
+ ...(index === 0
+ ? {
+ date: transaction.date,
+ reference_type: transaction.reference_type,
+ reference_id: transaction.reference_id,
+ reference_type_formatted: transaction.reference_type_formatted,
+ }
+ : {}),
+ rowType: TYPES.ENTRY,
+ ...entry,
+ }));
+ };
+
+ return chain(journal)
+ .map((transaction) => {
+ const entries = entriesMapper(transaction);
+
+ return [
+ ...entries,
+ {
+ rowType: TYPES.TOTAL_ENTRIES,
+ currency_code: transaction.currency_code,
+ credit: transaction.credit,
+ debit: transaction.debit,
+ formatted_credit: transaction.formatted_credit,
+ formatted_debit: transaction.formatted_debit,
+ },
+ {
+ rowType: TYPES.EMPTY_ROW,
+ },
+ ];
+ })
+ .flatten()
+ .value();
};
-
export const generalLedgerToTableRows = (accounts) => {
- return accounts.reduce((tableRows, account) => {
- const children = [];
- children.push({
- ...account.opening,
- rowType: 'opening_balance',
- });
- account.transactions.map((transaction) => {
- children.push({
- ...transaction,
- ...omit(account, ['transactions']),
- rowType: 'transaction',
- });
- });
- children.push({
- ...account.closing,
- rowType: 'closing_balance',
- });
- tableRows.push({
- ...omit(account, ['transactions']),
- children,
- rowType: 'account_name',
- });
- return tableRows;
- }, []);
+ return chain(accounts)
+ .map((account) => {
+ return {
+ name: '',
+ code: account.code,
+ rowType: 'ACCOUNT_ROW',
+ date: account.name,
+ children: [
+ {
+ ...account.opening_balance,
+ name: 'Opening balance',
+ rowType: 'OPENING_BALANCE',
+ },
+ ...account.transactions.map((transaction) => ({
+ ...transaction,
+ name: account.name,
+ code: account.code,
+ date: moment(transaction.date).format('DD MMM YYYY'),
+ })),
+ {
+ ...account.closing_balance,
+ name: 'Closing balance',
+ rowType: 'CLOSING_BALANCE',
+ },
+ ],
+ };
+ })
+ .value();
};
export const ARAgingSummaryTableRowsMapper = (sheet, total) => {
@@ -109,25 +131,32 @@ export const ARAgingSummaryTableRowsMapper = (sheet, total) => {
current: sheet.total.current.formatted_amount,
...mapAging(sheet.total.aging),
total: sheet.total.total.formatted_amount,
- }
- ];
-};
-
-export const mapTrialBalanceSheetToRows = (sheet) => {
- return [
- ...sheet.accounts,
- {
- name: 'Total',
- rowTypes: ['total'],
- ...sheet.total,
},
];
};
-export const profitLossToTableRowsMapper = (profitLoss) => {
+export const mapTrialBalanceSheetToRows = (sheet) => {
+ const results = [];
- return [
- {
+ if (sheet.accounts) {
+ sheet.accounts.forEach((account) => {
+ results.push(account);
+ });
+ }
+ if (sheet.total) {
+ results.push({
+ rowType: 'total',
+ ...sheet.total,
+ });
+ }
+ return results;
+};
+
+export const profitLossToTableRowsMapper = (profitLoss) => {
+ const results = [];
+
+ if (profitLoss.income) {
+ results.push({
name: 'Income',
total: profitLoss.income.total,
children: [
@@ -140,8 +169,10 @@ export const profitLossToTableRowsMapper = (profitLoss) => {
},
],
total_periods: profitLoss.income.total_periods,
- },
- {
+ });
+ }
+ if (profitLoss.cost_of_sales) {
+ results.push({
name: 'Cost of sales',
total: profitLoss.cost_of_sales.total,
children: [
@@ -153,15 +184,19 @@ export const profitLossToTableRowsMapper = (profitLoss) => {
rowTypes: ['cogs_total', 'section_total', 'total'],
},
],
- total_periods: profitLoss.cost_of_sales.total_periods
- },
- {
+ total_periods: profitLoss.cost_of_sales.total_periods,
+ });
+ }
+ if (profitLoss.gross_profit) {
+ results.push({
name: 'Gross profit',
total: profitLoss.gross_profit.total,
total_periods: profitLoss.gross_profit.total_periods,
rowTypes: ['gross_total', 'section_total', 'total'],
- },
- {
+ })
+ }
+ if (profitLoss.expenses) {
+ results.push({
name: 'Expenses',
total: profitLoss.expenses.total,
children: [
@@ -174,14 +209,34 @@ export const profitLossToTableRowsMapper = (profitLoss) => {
},
],
total_periods: profitLoss.expenses.total_periods,
- },
- {
+ })
+ }
+ if (profitLoss.operating_profit) {
+ results.push({
name: 'Net Operating income',
total: profitLoss.operating_profit.total,
total_periods: profitLoss.income.total_periods,
rowTypes: ['net_operating_total', 'section_total', 'total'],
- },
- {
+ })
+ }
+ if (profitLoss.other_income) {
+ results.push({
+ name: 'Other Income',
+ total: profitLoss.other_income.total,
+ total_periods: profitLoss.other_income.total_periods,
+ children: [
+ ...profitLoss.other_income.accounts,
+ {
+ name: 'Total other income',
+ total: profitLoss.other_income.total,
+ total_periods: profitLoss.other_income.total_periods,
+ rowTypes: ['expenses_total', 'section_total', 'total'],
+ },
+ ],
+ });
+ }
+ if (profitLoss.other_expenses) {
+ results.push({
name: 'Other expenses',
total: profitLoss.other_expenses.total,
total_periods: profitLoss.other_expenses.total_periods,
@@ -194,12 +249,15 @@ export const profitLossToTableRowsMapper = (profitLoss) => {
rowTypes: ['expenses_total', 'section_total', 'total'],
},
],
- },
- {
+ });
+ }
+ if (profitLoss.net_income) {
+ results.push({
name: 'Net Income',
total: profitLoss.net_income.total,
total_periods: profitLoss.net_income.total_periods,
rowTypes: ['net_income_total', 'section_total', 'total'],
- },
- ];
-};
\ No newline at end of file
+ })
+ };
+ return results;
+};
diff --git a/client/src/style/components/DataTable/DataTable.scss b/client/src/style/components/DataTable/DataTable.scss
index c9fb01445..1724bb489 100644
--- a/client/src/style/components/DataTable/DataTable.scss
+++ b/client/src/style/components/DataTable/DataTable.scss
@@ -27,6 +27,12 @@
color: #58667b;
font-weight: 500;
border-bottom: 1px solid rgb(224, 224, 224);
+
+ > div{
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
}
.sort-icon {
width: 0;
@@ -141,10 +147,23 @@
.placeholder {
color: #a0a0a0;
}
+ .text-overview{
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+
+ }
.bp3-form-group {
width: 100%;
}
+
+ &.is-text-overview {
+ .expend-padding{
+ display: flex;
+ width: 100%;
+ }
+ }
}
.tr:hover .td {
background: #f3f7fc;
diff --git a/client/src/style/containers/FinancialStatements/FinancialSheet.scss b/client/src/style/containers/FinancialStatements/FinancialSheet.scss
index b2bf7c1e4..18b3c7957 100644
--- a/client/src/style/containers/FinancialStatements/FinancialSheet.scss
+++ b/client/src/style/containers/FinancialStatements/FinancialSheet.scss
@@ -60,12 +60,17 @@
display: none;
}
}
- &__basis {
+ &__footer {
color: #888;
text-align: center;
margin-top: auto;
padding-top: 18px;
font-size: 13px;
+
+
+ > span + span{
+ padding-left: 10px;
+ }
}
.dashboard__loading-indicator {
margin: auto;
diff --git a/client/src/style/pages/FinancialStatements/ARAgingSummary.scss b/client/src/style/pages/FinancialStatements/ARAgingSummary.scss
index c331c9511..afdc364cc 100644
--- a/client/src/style/pages/FinancialStatements/ARAgingSummary.scss
+++ b/client/src/style/pages/FinancialStatements/ARAgingSummary.scss
@@ -13,20 +13,22 @@
}
}
.tbody{
- .tr .td{
- border-bottom: 0;
- padding-top: 0.4rem;
- padding-bottom: 0.4rem;
- }
- .tr:not(:first-child) .td{
- border-top: 1px solid transparent;
- }
- .tr.row-type--total{
- font-weight: 500;
-
+ .tr:not(.no-results) {
.td{
- border-top: 1px solid #333;
- border-bottom: 3px double #333;
+ border-bottom: 0;
+ padding-top: 0.4rem;
+ padding-bottom: 0.4rem;
+ }
+ &.row-type--total{
+ font-weight: 500;
+
+ .td{
+ border-top: 1px solid #333;
+ border-bottom: 3px double #333;
+ }
+ }
+ &:not(:first-child) .td{
+ border-top: 1px solid transparent;
}
}
}
diff --git a/client/src/style/pages/FinancialStatements/BalanceSheet.scss b/client/src/style/pages/FinancialStatements/BalanceSheet.scss
index 35e909077..e145fd445 100644
--- a/client/src/style/pages/FinancialStatements/BalanceSheet.scss
+++ b/client/src/style/pages/FinancialStatements/BalanceSheet.scss
@@ -36,7 +36,7 @@
.tr.is-expanded{
.td.total,
.td.total-period{
- > span{
+ > span.cell-text{
display: none;
}
}
diff --git a/client/src/style/pages/FinancialStatements/FinancialSheet.scss b/client/src/style/pages/FinancialStatements/FinancialSheet.scss
index f3e7530ba..5c37bfdd3 100644
--- a/client/src/style/pages/FinancialStatements/FinancialSheet.scss
+++ b/client/src/style/pages/FinancialStatements/FinancialSheet.scss
@@ -3,9 +3,17 @@
&--financial-report{
.table {
+ .tbody{
+
+ .tr.no-results {
+ .td{
+ border-bottom: 1px solid #DDD;
+ }
+ }
+ }
.thead{
.tr .th{
- background: transparent;
+ background-color: #fff;
border-top: 1px solid #666;
border-bottom: 1px solid #666;
diff --git a/client/src/style/pages/FinancialStatements/GeneralLedger.scss b/client/src/style/pages/FinancialStatements/GeneralLedger.scss
index f3fd1fa6f..96dd9a8a9 100644
--- a/client/src/style/pages/FinancialStatements/GeneralLedger.scss
+++ b/client/src/style/pages/FinancialStatements/GeneralLedger.scss
@@ -2,25 +2,64 @@
.financial-sheet{
&--general-ledger{
.financial-sheet__table{
+ .tbody,
+ .thead{
+ .tr .td,
+ .tr .th{
+ &.credit,
+ &.debit,
+ &.running_balance,
+ &.amount{
+ justify-content: flex-end;
+ }
+ }
+ }
.tbody{
+
+ .tr .td{
+ padding-top: 0.2rem;
+ padding-bottom: 0.2rem;
+ border-top-color: transparent;
+ border-bottom-color: transparent;
+
+ &.date{
+ > div{
+ display: flex;
+ }
+ span.force-width{
+ position: relative;
+ }
+ }
+ }
+ .tr:not(.no-results) .td{
+ border-left: 1px solid #ececec;
+ }
.tr.row-type{
- &--opening_balance,
- &--closing_balance{
+
+ &--ACCOUNT_ROW{
.td{
- border-top: 1px solid #333;
+ &.date{
+ font-weight: 500;
+ }
+ &.name{
+ border-left-color: transparent;
+ }
}
- .name,
- .amount,
- .balance{
+ &:not(:first-child).is-expanded .td{
+ border-top: 1px solid #DDD;
+ }
+ }
+ &--OPENING_BALANCE,
+ &--CLOSING_BALANCE{
+ .amount{
font-weight: 500;
}
}
- &--closing_balance .td{
- border-bottom-color: #666;
- }
- &--account_name .td.name{
- font-weight: 500;
+ &--CLOSING_BALANCE{
+ .name{
+ font-weight: 500;
+ }
}
}
}
diff --git a/client/src/style/pages/FinancialStatements/Journal.scss b/client/src/style/pages/FinancialStatements/Journal.scss
index 7447d752b..cd7792e9b 100644
--- a/client/src/style/pages/FinancialStatements/Journal.scss
+++ b/client/src/style/pages/FinancialStatements/Journal.scss
@@ -3,19 +3,39 @@
&--journal{
.financial-sheet__table{
-
+ .tr .td.credit,
+ .tr .th.credit,
+ .tr .td.debit,
+ .tr .th.debit{
+ justify-content: flex-end;
+ }
.tbody{
.tr:not(.no-results) .td{
- padding: 0.4rem;
+ padding: 0.3rem 0.4rem;
color: #000;
border-bottom-color: transparent;
- min-height: 32px;
+ min-height: 28px;
border-left: 1px solid #ececec;
&:first-of-type{
border-left: 0;
}
+ &.account_name,
+ &.reference_type_formatted{
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow:ellipsis;
+ }
}
+ .tr:not(.no-results):last-child{
+ .td{
+ border-bottom: 1px solid #dbdbdb;
+ }
+ }
+ .tr.row_type--TOTAL_ENTRIES{
+ font-weight: 600;
+ }
+
}
}
}
diff --git a/client/src/utils.js b/client/src/utils.js
index 0ba62b39f..a68ea7bae 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -389,6 +389,16 @@ export const getColumnWidth = (
return result;
};
+export const getForceWidth = (
+ text,
+ magicSpacing = 14,
+) => {
+ const textLength = text.length;
+ const result = textLength * magicSpacing
+
+ return result;
+}
+
export const toSafeNumber = (number) => {
return _.toNumber(_.defaultTo(number, 0));
};
diff --git a/server/src/api/controllers/FinancialStatements/JournalSheet.ts b/server/src/api/controllers/FinancialStatements/JournalSheet.ts
index f832fedf6..350a80dbb 100644
--- a/server/src/api/controllers/FinancialStatements/JournalSheet.ts
+++ b/server/src/api/controllers/FinancialStatements/JournalSheet.ts
@@ -16,7 +16,8 @@ export default class JournalSheetController extends BaseFinancialReportControlle
router() {
const router = Router();
- router.get('/',
+ router.get(
+ '/',
this.journalValidationSchema,
this.validationResult,
this.asyncMiddleware(this.journal.bind(this))
@@ -31,18 +32,20 @@ export default class JournalSheetController extends BaseFinancialReportControlle
return [
query('from_date').optional().isISO8601(),
query('to_date').optional().isISO8601(),
- oneOf([
- query('transaction_types').optional().isArray({ min: 1 }),
- query('transaction_types.*').optional().isNumeric().toInt(),
- ], [
- query('transaction_types').optional().trim().escape(),
- ]),
- oneOf([
- query('account_ids').optional().isArray({ min: 1 }),
- query('account_ids.*').optional().isNumeric().toInt(),
- ], [
- query('account_ids').optional().isNumeric().toInt(),
- ]),
+ oneOf(
+ [
+ query('transaction_types').optional().isArray({ min: 1 }),
+ query('transaction_types.*').optional().isNumeric().toInt(),
+ ],
+ [query('transaction_types').optional().trim().escape()]
+ ),
+ oneOf(
+ [
+ query('account_ids').optional().isArray({ min: 1 }),
+ query('account_ids.*').optional().isNumeric().toInt(),
+ ],
+ [query('account_ids').optional().isNumeric().toInt()]
+ ),
query('from_range').optional().isNumeric().toInt(),
query('to_range').optional().isNumeric().toInt(),
query('number_format.no_cents').optional().isBoolean().toBoolean(),
@@ -52,7 +55,7 @@ export default class JournalSheetController extends BaseFinancialReportControlle
/**
* Retrieve the ledger report of the given account.
- * @param {Request} req -
+ * @param {Request} req -
* @param {Response} res -
*/
async journal(req: Request, res: Response, next: NextFunction) {
@@ -63,11 +66,20 @@ export default class JournalSheetController extends BaseFinancialReportControlle
...filter,
accountsIds: castArray(filter.accountsIds),
};
- const organizationName = settings.get({ group: 'organization', key: 'name' });
- const baseCurrency = settings.get({ group: 'organization', key: 'base_currency' });
+ const organizationName = settings.get({
+ group: 'organization',
+ key: 'name',
+ });
+ const baseCurrency = settings.get({
+ group: 'organization',
+ key: 'base_currency',
+ });
try {
- const { data, query } = await this.journalService.journalSheet(tenantId, filter);
+ const { data, query } = await this.journalService.journalSheet(
+ tenantId,
+ filter
+ );
return res.status(200).send({
organization_name: organizationName,
@@ -79,4 +91,4 @@ export default class JournalSheetController extends BaseFinancialReportControlle
next(error);
}
}
-}
\ No newline at end of file
+}
diff --git a/server/src/api/controllers/InviteUsers.ts b/server/src/api/controllers/InviteUsers.ts
index 831f995a6..1a78e41c1 100644
--- a/server/src/api/controllers/InviteUsers.ts
+++ b/server/src/api/controllers/InviteUsers.ts
@@ -1,6 +1,7 @@
import { Service, Inject } from 'typedi';
import { Router, Request, Response } from 'express';
import { check, body, param } from 'express-validator';
+import { IInviteUserInput } from 'interfaces';
import asyncMiddleware from 'api/middleware/asyncMiddleware';
import InviteUserService from 'services/InviteUsers';
import { ServiceErrors, ServiceError } from 'exceptions';
@@ -22,7 +23,7 @@ export default class InviteUsersController extends BaseController {
[body('email').exists().trim().escape()],
this.validationResult,
asyncMiddleware(this.sendInvite.bind(this)),
- this.handleServicesError,
+ this.handleServicesError
);
return router;
}
@@ -38,14 +39,14 @@ export default class InviteUsersController extends BaseController {
[...this.inviteUserDTO],
this.validationResult,
asyncMiddleware(this.accept.bind(this)),
- this.handleServicesError,
+ this.handleServicesError
);
router.get(
'/invited/:token',
[param('token').exists().trim().escape()],
this.validationResult,
asyncMiddleware(this.invited.bind(this)),
- this.handleServicesError,
+ this.handleServicesError
);
return router;
@@ -76,8 +77,11 @@ export default class InviteUsersController extends BaseController {
const { user } = req;
try {
- await this.inviteUsersService.sendInvite(tenantId, email, user);
-
+ const { invite } = await this.inviteUsersService.sendInvite(
+ tenantId,
+ email,
+ user
+ );
return res.status(200).send({
type: 'success',
code: 'INVITE.SENT.SUCCESSFULLY',
@@ -104,6 +108,7 @@ export default class InviteUsersController extends BaseController {
try {
await this.inviteUsersService.acceptInvite(token, inviteUserInput);
+
return res.status(200).send({
type: 'success',
code: 'USER.INVITE.ACCEPTED',
@@ -144,19 +149,40 @@ export default class InviteUsersController extends BaseController {
*/
handleServicesError(error, req: Request, res: Response, next: Function) {
if (error instanceof ServiceError) {
+ if (error.errorType === 'EMAIL_EXISTS') {
+ return res.status(400).send({
+ errors: [{
+ type: 'EMAIL.ALREADY.EXISTS',
+ code: 100,
+ message: 'Email already exists in the users.'
+ }],
+ });
+ }
if (error.errorType === 'EMAIL_ALREADY_INVITED') {
return res.status(400).send({
- errors: [{ type: 'EMAIL.ALREADY.INVITED' }],
+ errors: [{
+ type: 'EMAIL.ALREADY.INVITED',
+ code: 200,
+ message: 'Email already invited.',
+ }],
});
}
if (error.errorType === 'INVITE_TOKEN_INVALID') {
return res.status(400).send({
- errors: [{ type: 'INVITE.TOKEN.INVALID' }],
+ errors: [{
+ type: 'INVITE.TOKEN.INVALID',
+ code: 300,
+ message: 'Invite token is invalid, please try another one.',
+ }],
});
}
if (error.errorType === 'PHONE_NUMBER_EXISTS') {
return res.status(400).send({
- errors: [{ type: 'PHONE_NUMBER.EXISTS' }],
+ errors: [{
+ type: 'PHONE_NUMBER.EXISTS',
+ code: 400,
+ message: 'Phone number is already invited, please try another unique one.'
+ }],
});
}
}
diff --git a/server/src/api/controllers/Ping.ts b/server/src/api/controllers/Ping.ts
index 0dd89f3f0..df199d8c2 100644
--- a/server/src/api/controllers/Ping.ts
+++ b/server/src/api/controllers/Ping.ts
@@ -2,7 +2,7 @@ import { Router, Request, Response } from 'express';
export default class Ping {
/**
- * Router constur
+ * Router constructor.
*/
router() {
const router = Router();
diff --git a/server/src/api/controllers/Users.ts b/server/src/api/controllers/Users.ts
index ba83d2465..ef35a71f7 100644
--- a/server/src/api/controllers/Users.ts
+++ b/server/src/api/controllers/Users.ts
@@ -126,6 +126,7 @@ export default class UsersController extends BaseController{
try {
await this.usersService.deleteUser(tenantId, id);
+
return res.status(200).send({
id,
message: 'The user has been deleted successfully.'
@@ -225,10 +226,10 @@ export default class UsersController extends BaseController{
if (error instanceof ServiceErrors) {
const errorReasons = [];
- if (error.errorType === 'email_already_exists') {
+ if (error.errorType === 'EMAIL_ALREADY_EXISTS') {
errorReasons.push({ type: 'EMAIL_ALREADY_EXIST', code: 100 });
}
- if (error.errorType === 'phone_number_already_exist') {
+ if (error.errorType === 'PHONE_NUMBER_ALREADY_EXIST') {
errorReasons.push({ type: 'PHONE_NUMBER_ALREADY_EXIST', code: 200 });
}
if (errorReasons.length > 0) {
@@ -236,30 +237,36 @@ export default class UsersController extends BaseController{
}
}
if (error instanceof ServiceError) {
- if (error.errorType === 'user_not_found') {
+ if (error.errorType === 'USER_NOT_FOUND') {
return res.boom.badRequest(
'User not found.',
{ errors: [{ type: 'USER.NOT.FOUND', code: 100 }] }
);
}
- if (error.errorType === 'user_already_active') {
+ if (error.errorType === 'USER_ALREADY_ACTIVE') {
return res.boom.badRequest(
'User is already active.',
{ errors: [{ type: 'USER.ALREADY.ACTIVE', code: 200 }] },
);
}
- if (error.errorType === 'user_already_inactive') {
+ if (error.errorType === 'USER_ALREADY_INACTIVE') {
return res.boom.badRequest(
'User is already inactive.',
{ errors: [{ type: 'USER.ALREADY.INACTIVE', code: 200 }] },
);
}
- if (error.errorType === 'user_same_the_authorized_user') {
+ if (error.errorType === 'USER_SAME_THE_AUTHORIZED_USER') {
return res.boom.badRequest(
'You could not activate/inactivate the same authorized user.',
{ errors: [{ type: 'CANNOT.TOGGLE.ACTIVATE.AUTHORIZED.USER', code: 300 }] },
)
}
+ if (error.errorType === 'CANNOT_DELETE_LAST_USER') {
+ return res.boom.badRequest(
+ 'Cannot delete last user in the organization.',
+ { errors: [{ type: 'CANNOT_DELETE_LAST_USER', code: 400 }] },
+ );
+ }
}
next(error);
}
diff --git a/server/src/api/middleware/AttachCurrentTenantUser.ts b/server/src/api/middleware/AttachCurrentTenantUser.ts
index 0fc1d9034..0ba44f689 100644
--- a/server/src/api/middleware/AttachCurrentTenantUser.ts
+++ b/server/src/api/middleware/AttachCurrentTenantUser.ts
@@ -14,7 +14,6 @@ const attachCurrentUser = async (req: Request, res: Response, next: Function) =>
try {
Logger.info('[attach_user_middleware] finding system user by id.');
const user = await systemUserRepository.findOneById(req.token.id);
- console.log(user);
if (!user) {
Logger.info('[attach_user_middleware] the system user not found.');
diff --git a/server/src/api/middleware/EnsureTenantIsSeeded.ts b/server/src/api/middleware/EnsureTenantIsSeeded.ts
index 20d31e690..69f92c76a 100644
--- a/server/src/api/middleware/EnsureTenantIsSeeded.ts
+++ b/server/src/api/middleware/EnsureTenantIsSeeded.ts
@@ -9,11 +9,13 @@ export default (req: Request, res: Response, next: Function) => {
throw new Error('Should load this middleware after `TenancyMiddleware`.');
}
if (!req.tenant.seededAt) {
- Logger.info('[ensure_tenant_initialized_middleware] tenant databae not seeded.');
+ Logger.info(
+ '[ensure_tenant_initialized_middleware] tenant databae not seeded.'
+ );
return res.boom.badRequest(
'Tenant database is not seeded with initial data yet.',
- { errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] },
+ { errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] }
);
}
next();
-};
\ No newline at end of file
+};
diff --git a/server/src/api/middleware/SubscriptionMiddleware.ts b/server/src/api/middleware/SubscriptionMiddleware.ts
index 17e2ee5c8..ce7d45258 100644
--- a/server/src/api/middleware/SubscriptionMiddleware.ts
+++ b/server/src/api/middleware/SubscriptionMiddleware.ts
@@ -1,7 +1,11 @@
import { Request, Response, NextFunction } from 'express';
import { Container } from 'typedi';
-export default (subscriptionSlug = 'main') => async (req: Request, res: Response, next: NextFunction) => {
+export default (subscriptionSlug = 'main') => async (
+ req: Request,
+ res: Response,
+ next: NextFunction
+) => {
const { tenant, tenantId } = req;
const Logger = Container.get('logger');
const { subscriptionRepository } = Container.get('repositories');
@@ -10,22 +14,28 @@ export default (subscriptionSlug = 'main') => async (req: Request, res: Response
throw new Error('Should load `TenancyMiddlware` before this middleware.');
}
Logger.info('[subscription_middleware] trying get tenant main subscription.');
- const subscription = await subscriptionRepository.getBySlugInTenant(subscriptionSlug, tenantId);
-
+ const subscription = await subscriptionRepository.getBySlugInTenant(
+ subscriptionSlug,
+ tenantId
+ );
// Validate in case there is no any already subscription.
if (!subscription) {
- Logger.info('[subscription_middleware] tenant has no subscription.', { tenantId });
- return res.boom.badRequest(
- 'Tenant has no subscription.',
- { errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }] }
- );
+ Logger.info('[subscription_middleware] tenant has no subscription.', {
+ tenantId,
+ });
+ return res.boom.badRequest('Tenant has no subscription.', {
+ errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }],
+ });
}
// Validate in case the subscription is inactive.
else if (subscription.inactive()) {
- Logger.info('[subscription_middleware] tenant main subscription is expired.', { tenantId });
+ Logger.info(
+ '[subscription_middleware] tenant main subscription is expired.',
+ { tenantId }
+ );
return res.boom.badRequest(null, {
errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }],
});
}
next();
-};
\ No newline at end of file
+};
diff --git a/server/src/api/middleware/asyncMiddleware.ts b/server/src/api/middleware/asyncMiddleware.ts
index a8c0f747b..ece9dd2cf 100644
--- a/server/src/api/middleware/asyncMiddleware.ts
+++ b/server/src/api/middleware/asyncMiddleware.ts
@@ -1,5 +1,3 @@
-import logger from "src/loaders/logger";
-
import { Request, Response, NextFunction } from 'express';
import { Container } from 'typedi';
diff --git a/server/src/collection/SoftDeleteQueryBuilder.js b/server/src/collection/SoftDeleteQueryBuilder.js
new file mode 100644
index 000000000..2bd15fe30
--- /dev/null
+++ b/server/src/collection/SoftDeleteQueryBuilder.js
@@ -0,0 +1,73 @@
+import moment from 'moment';
+import { Model } from 'objection';
+
+const options = {
+ columnName: 'deleted_at',
+ deletedValue: moment().format('YYYY-MM-DD HH:mm:ss'),
+ notDeletedValue: null,
+};
+
+export default class SoftDeleteQueryBuilder extends Model.QueryBuilder {
+ constructor(...args) {
+ super(...args);
+
+ this.onBuild((builder) => {
+ if (builder.isFind() || builder.isDelete() || builder.isUpdate()) {
+ builder.whereNotDeleted();
+ }
+ });
+ }
+
+ /**
+ * override the normal delete function with one that patches the row's "deleted" column
+ */
+ delete() {
+ this.context({
+ softDelete: true,
+ });
+ const patch = {};
+ patch[options.columnName] = options.deletedValue;
+ return this.patch(patch);
+ }
+
+ /**
+ * Provide a way to actually delete the row if necessary
+ */
+ hardDelete() {
+ return super.delete();
+ }
+
+ /**
+ * Provide a way to undo the delete
+ */
+ undelete() {
+ this.context({
+ undelete: true,
+ });
+ const patch = {};
+ patch[options.columnName] = options.notDeletedValue;
+ return this.patch(patch);
+ }
+
+ /**
+ * Provide a way to filter to ONLY deleted records without having to remember the column name
+ */
+ whereDeleted() {
+ const prefix = this.modelClass().tableName;
+
+ // this if is for backwards compatibility, to protect those that used a nullable `deleted` field
+ if (options.deletedValue === true) {
+ return this.where(`${prefix}.${options.columnName}`, options.deletedValue);
+ }
+ // qualify the column name
+ return this.whereNot(`${prefix}.${options.columnName}`, options.notDeletedValue);
+ }
+
+ // provide a way to filter out deleted records without having to remember the column name
+ whereNotDeleted() {
+ const prefix = this.modelClass().tableName;
+
+ // qualify the column name
+ return this.where(`${prefix}.${options.columnName}`, options.notDeletedValue);
+ }
+}
diff --git a/server/src/data/BalanceSheetStructure.ts b/server/src/data/BalanceSheetStructure.ts
index d57a58d9c..bdb785000 100644
--- a/server/src/data/BalanceSheetStructure.ts
+++ b/server/src/data/BalanceSheetStructure.ts
@@ -8,19 +8,42 @@ const balanceSheetStructure: IBalanceSheetStructureSection[] = [
children: [
{
name: 'Current Asset',
- type: 'accounts_section',
- accountsTypesRelated: ['current_asset'],
+ sectionType: 'assets',
+ type: 'section',
+ children: [
+ {
+ name: 'Cash and cash equivalents',
+ type: 'accounts_section',
+ accountsTypes: ['cash', 'bank'],
+ },
+ {
+ name: 'Accounts Receivable',
+ type: 'accounts_section',
+ accountsTypes: ['accounts_receivable'],
+ },
+ {
+ name: 'Inventories',
+ type: 'accounts_section',
+ accountsTypes: ['inventory'],
+ },
+ {
+ name: 'Other current assets',
+ type: 'accounts_section',
+ accountsTypes: ['other_current_asset'],
+ },
+ ],
+ alwaysShow: true,
},
{
name: 'Fixed Asset',
type: 'accounts_section',
- accountsTypesRelated: ['fixed_asset'],
+ accountsTypes: ['fixed_asset'],
},
{
- name: 'Other Asset',
+ name: 'Non-Current Assets',
type: 'accounts_section',
- accountsTypesRelated: ['other_asset'],
- },
+ accountsTypes: ['non_current_asset'],
+ }
],
alwaysShow: true,
},
@@ -35,27 +58,32 @@ const balanceSheetStructure: IBalanceSheetStructureSection[] = [
type: 'section',
children: [
{
- name: 'Current Liability',
+ name: 'Current Liabilties',
type: 'accounts_section',
- accountsTypesRelated: ['current_liability'],
+ accountsTypes: [
+ 'accounts_payable',
+ 'tax_payable',
+ 'credit_card',
+ 'other_current_liability'
+ ],
},
{
- name: 'Long Term Liability',
+ name: 'Long-Term Liabilities',
type: 'accounts_section',
- accountsTypesRelated: ['long_term_liability'],
+ accountsTypes: ['long_term_liability'],
},
{
- name: 'Other Liability',
+ name: 'Non-Current Liabilities',
type: 'accounts_section',
- accountsTypesRelated: ['other_liability'],
- },
+ accountsTypes: ['non_current_liability'],
+ }
],
},
{
name: 'Equity',
sectionType: 'equity',
type: 'accounts_section',
- accountsTypesRelated: ['equity'],
+ accountsTypes: ['equity'],
},
],
alwaysShow: true,
diff --git a/server/src/database/seeds/core/20190423085241_seed_accounts_types.js b/server/src/database/seeds/core/20190423085241_seed_accounts_types.js
index bd681092a..d4ba67987 100644
--- a/server/src/database/seeds/core/20190423085241_seed_accounts_types.js
+++ b/server/src/database/seeds/core/20190423085241_seed_accounts_types.js
@@ -1,141 +1,14 @@
import Container from 'typedi';
import TenancyService from 'services/Tenancy/TenancyService'
import I18nMiddleware from 'api/middleware/I18nMiddleware';
+import AccountsTypesData from '../data/accounts_types';
exports.up = function (knex) {
- const tenancyService = Container.get(TenancyService);
- const i18n = tenancyService.i18n(knex.userParams.tenantId);
-
return knex('account_types').insert([
- {
- id: 1,
- key: 'fixed_asset',
- normal: 'debit',
- root_type: 'asset',
- child_type: 'fixed_asset',
- balance_sheet: true,
- income_sheet: false,
- },
- {
- id: 2,
- key: 'current_asset',
- normal: 'debit',
- root_type: 'asset',
- child_type: 'current_asset',
- balance_sheet: true,
- income_sheet: false,
- },
- {
- id: 14,
- key: 'other_asset',
- normal: 'debit',
- root_type: 'asset',
- child_type: 'other_asset',
- balance_sheet: true,
- income_sheet: false,
- },
- {
- id: 3,
- key: 'long_term_liability',
- normal: 'credit',
- root_type: 'liability',
- child_type: 'long_term_liability',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 4,
- key: 'current_liability',
- normal: 'credit',
- root_type: 'liability',
- child_type: 'current_liability',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 13,
- key: 'other_liability',
- normal: 'credit',
- root_type: 'liability',
- child_type: 'other_liability',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 5,
- key: 'equity',
- normal: 'credit',
- root_type: 'equity',
- child_type: 'equity',
- balance_sheet: true,
- income_sheet: false,
- },
- {
- id: 6,
- key: 'expense',
- normal: 'debit',
- root_type: 'expense',
- child_type: 'expense',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 10,
- key: 'other_expense',
- normal: 'debit',
- root_type: 'expense',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 7,
- key: 'income',
- normal: 'credit',
- root_type: 'income',
- child_type: 'income',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 11,
- key: 'other_income',
- normal: 'credit',
- root_type: 'income',
- child_type: 'other_income',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 12,
- key: 'cost_of_goods_sold',
- normal: 'debit',
- root_type: 'expenses',
- child_type: 'expenses',
- balance_sheet: false,
- income_sheet: true,
- },
- {
- id: 8,
- key: 'accounts_receivable',
- normal: 'debit',
- root_type: 'asset',
- child_type: 'current_asset',
- balance_sheet: true,
- income_sheet: false,
- },
- {
- id: 9,
- key: 'accounts_payable',
- normal: 'credit',
- root_type: 'liability',
- child_type: 'current_liability',
- balance_sheet: true,
- income_sheet: false,
- },
+ ...AccountsTypesData
]);
};
-
exports.down = function(knex) {
}
\ No newline at end of file
diff --git a/server/src/database/seeds/core/20190423085242_seed_accounts.js b/server/src/database/seeds/core/20190423085242_seed_accounts.js
index 270ad5504..6d0f86b95 100644
--- a/server/src/database/seeds/core/20190423085242_seed_accounts.js
+++ b/server/src/database/seeds/core/20190423085242_seed_accounts.js
@@ -1,179 +1,32 @@
import Container from 'typedi';
+import { get } from 'lodash';
import TenancyService from 'services/Tenancy/TenancyService'
+import AccountsData from '../data/accounts';
+
exports.up = function (knex) {
const tenancyService = Container.get(TenancyService);
const i18n = tenancyService.i18n(knex.userParams.tenantId);
- return knex('accounts').then(() => {
- // Inserts seed entries
- return knex('accounts').insert([
- {
- id: 1,
- name: i18n.__('Petty Cash'),
- slug: 'petty-cash',
- account_type_id: 2,
- parent_account_id: null,
- code: '1000',
- description: '',
+ const accountMapper = (account) => {
+ return knex('account_types').where('key', account.account_type).first()
+ .then((accountType) => ({
+ name: i18n.__(account.name),
+ slug: account.slug,
+ account_type_id: get(accountType, 'id', null),
+ code: account.code,
+ description: i18n.__(account.description),
active: 1,
index: 1,
- predefined: 1,
- },
- {
- id: 2,
- name: i18n.__('Bank'),
- slug: 'bank',
- account_type_id: 2,
- parent_account_id: null,
- code: '2000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 3,
- name: i18n.__('Other Income'),
- slug: 'other-income',
- account_type_id: 7,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 4,
- name: i18n.__('Interest Income'),
- slug: 'interest-income',
- account_type_id: 7,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 5,
- name: i18n.__('Opening Balance'),
- slug: 'opening-balance',
- account_type_id: 5,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 6,
- name: i18n.__('Depreciation Expense'),
- slug: 'depreciation-expense',
- account_type_id: 6,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 7,
- name: i18n.__('Interest Expense'),
- slug: 'interest-expense',
- account_type_id: 6,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 8,
- name: i18n.__('Payroll Expenses'),
- slug: 'payroll-expenses',
- account_type_id: 6,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 9,
- name: i18n.__('Other Expenses'),
- slug: 'other-expenses',
- account_type_id: 6,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 10,
- name: i18n.__('Accounts Receivable'),
- slug: 'accounts-receivable',
- account_type_id: 8,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 11,
- name: i18n.__('Accounts Payable'),
- slug: 'accounts-payable',
- account_type_id: 9,
- parent_account_id: null,
- code: '1000',
- description: '',
- active: 1,
- index: 1,
- predefined: 1,
- },
- {
- id: 12,
- name: i18n.__('Cost of Goods Sold (COGS)'),
- slug: 'cost-of-goods-sold',
- account_type_id: 12,
- predefined: 1,
- parent_account_id: null,
- index: 1,
- active: 1,
- description: 1,
- },
- {
- id: 13,
- name: i18n.__('Inventory Asset'),
- slug: 'inventory-asset',
- account_type_id: 14,
- predefined: 1,
- parent_account_id: null,
- index: 1,
- active: 1,
- description: '',
- },
- {
- id: 14,
- name: i18n.__('Sales of Product Income'),
- slug: 'sales-of-product-income',
- account_type_id: 7,
- predefined: 1,
- parent_account_id: null,
- index: 1,
- active: 1,
- description: '',
- }
- ]);
+ predefined: account.predefined,
+ }));
+ };
+ return knex('accounts').then(async () => {
+ const accountsPromises = AccountsData.map(accountMapper);
+ const accounts = await Promise.all(accountsPromises);
+
+ // Inserts seed entries.
+ return knex('accounts').insert([ ...accounts ]);
});
};
diff --git a/server/src/database/seeds/data/accounts.js b/server/src/database/seeds/data/accounts.js
new file mode 100644
index 000000000..2f61ad79a
--- /dev/null
+++ b/server/src/database/seeds/data/accounts.js
@@ -0,0 +1,318 @@
+
+export default [
+ {
+ name:'Bank Account',
+ slug: 'bank-account',
+ account_type: 'bank',
+ code: '10001',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Saving Bank Account',
+ slug: 'saving-bank-account',
+ account_type: 'bank',
+ code: '10002',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Undeposited Funds',
+ slug: 'undeposited-funds',
+ account_type: 'cash',
+ code: '10003',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Petty Cash',
+ slug: 'petty-cash',
+ account_type: 'cash',
+ code: '10004',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Computer Equipment',
+ slug: 'computer-equipment',
+ code: '10005',
+ account_type: 'fixed_asset',
+ predefined: 0,
+ parent_account_id: null,
+ index: 1,
+ active: 1,
+ description: '',
+ },
+ {
+ name:'Office Equipment',
+ slug: 'office-equipment',
+ code: '10006',
+ account_type: 'fixed_asset',
+ predefined: 0,
+ parent_account_id: null,
+ index: 1,
+ active: 1,
+ description: '',
+ },
+ {
+ name:'Accounts Receivable (A/R)',
+ slug: 'accounts-receivable',
+ account_type: 'accounts_receivable',
+ code: '10007',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Inventory Asset',
+ slug: 'inventory-asset',
+ code: '10008',
+ account_type: 'inventory',
+ predefined: 1,
+ parent_account_id: null,
+ index: 1,
+ active: 1,
+ description:'An account that holds valuation of products or goods that availiable for sale.',
+ },
+
+ // Libilities
+ {
+ name:'Accounts Payable (A/P)',
+ slug: 'accounts-payable',
+ account_type: 'accounts_payable',
+ parent_account_id: null,
+ code: '20001',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Owner A Drawings',
+ slug: 'owner-drawings',
+ account_type: 'other_current_liability',
+ parent_account_id: null,
+ code: '20002',
+ description:'Withdrawals by the owners.',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Loan',
+ slug: 'owner-drawings',
+ account_type: 'other_current_liability',
+ code: '20003',
+ description:'Money that has been borrowed from a creditor.',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Opening Balance Adjustments',
+ slug: 'opening-balance-adjustments',
+ account_type: 'other_current_liability',
+ code: '20004',
+ description:'This account will hold the difference in the debits and credits entered during the opening balance..',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Revenue Received in Advance',
+ slug: 'Revenue-received-in-advance',
+ account_type: 'other_current_liability',
+ parent_account_id: null,
+ code: '20005',
+ description: 'When customers pay in advance for products/services.',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Sales Tax Payable',
+ slug: 'owner-drawings',
+ account_type: 'tax_payable',
+ code: '20006',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+
+ // Equity
+ {
+ name:'Retained Earnings',
+ slug: 'retained-earnings',
+ account_type: 'equity',
+ code: '30001',
+ description:'Retained earnings tracks net income from previous fiscal years.',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Opening Balance Equity',
+ slug: 'opening-balance-equity',
+ account_type: 'equity',
+ code: '30002',
+ description:'When you enter opening balances to the accounts, the amounts enter in Opening balance equity. This ensures that you have a correct trial balance sheet for your company, without even specific the second credit or debit entry.',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name: "Owner's Equity",
+ slug: 'owner-equity',
+ account_type: 'equity',
+ code: '30003',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:`Drawings`,
+ slug: 'drawings',
+ account_type: 'equity',
+ code: '30003',
+ description:'Goods purchased with the intention of selling these to customers',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+
+ // Expenses
+ {
+ name:'Uncategorized Expenses',
+ slug: 'uncategorized-expense',
+ account_type: 'expense',
+ parent_account_id: null,
+ code: '40001',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Cost of Goods Sold',
+ slug: 'cost-of-goods-sold',
+ account_type: 'cost_of_goods_sold',
+ parent_account_id: null,
+ code: '40002',
+ description:'Tracks the direct cost of the goods sold.',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Office expenses',
+ slug: 'office-expenses',
+ account_type: 'expense',
+ parent_account_id: null,
+ code: '40003',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Rent',
+ slug: 'rent',
+ account_type: 'expense',
+ parent_account_id: null,
+ code: '40004',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Exchange Gain or Loss',
+ slug: 'exchange-grain-loss',
+ account_type: 'other_expense',
+ parent_account_id: null,
+ code: '40005',
+ description:'Tracks the gain and losses of the exchange differences.',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Bank Fees and Charges',
+ slug: 'bank-fees-and-charges',
+ account_type: 'expense',
+ parent_account_id: null,
+ code: '40006',
+ description: 'Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+ {
+ name:'Depreciation Expense',
+ slug: 'depreciation-expense',
+ account_type: 'expense',
+ parent_account_id: null,
+ code: '40007',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ },
+
+ // Income
+ {
+ name:'Sales of Product Income',
+ slug: 'sales-of-product-income',
+ account_type: 'income',
+ predefined: 1,
+ parent_account_id: null,
+ code: '50001',
+ index: 1,
+ active: 1,
+ description: '',
+ },
+ {
+ name:'Sales of Service Income',
+ slug: 'sales-of-service-income',
+ account_type: 'income',
+ predefined: 0,
+ parent_account_id: null,
+ code: '50002',
+ index: 1,
+ active: 1,
+ description: '',
+ },
+ {
+ name:'Uncategorized Income',
+ slug: 'uncategorized-income',
+ account_type: 'income',
+ parent_account_id: null,
+ code: '50003',
+ description: '',
+ active: 1,
+ index: 1,
+ predefined: 1,
+ },
+ {
+ name:'Other Income',
+ slug: 'other-income',
+ account_type: 'other_income',
+ parent_account_id: null,
+ code: '50004',
+ description:'The income activities are not associated to the core business.',
+ active: 1,
+ index: 1,
+ predefined: 0,
+ }
+];
\ No newline at end of file
diff --git a/server/src/database/seeds/data/accounts_types.js b/server/src/database/seeds/data/accounts_types.js
new file mode 100644
index 000000000..b38da463a
--- /dev/null
+++ b/server/src/database/seeds/data/accounts_types.js
@@ -0,0 +1,155 @@
+
+
+export default [
+ {
+ key: 'cash',
+ normal: 'debit',
+ child_type: 'current_asset',
+ root_type: 'asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'bank',
+ normal: 'debit',
+ child_type: 'current_asset',
+ root_type: 'asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'accounts_receivable',
+ normal: 'debit',
+ root_type: 'asset',
+ child_type: 'current_asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'inventory',
+ normal: 'debit',
+ root_type: 'asset',
+ child_type: 'current_asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'other_current_asset',
+ normal: 'debit',
+ root_type: 'asset',
+ child_type: 'current_asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'fixed_asset',
+ normal: 'debit',
+ root_type: 'asset',
+ child_type: 'fixed_asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'non_current_asset',
+ normal: 'debit',
+ root_type: 'asset',
+ child_type: 'fixed_asset',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'accounts_payable',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'current_liability',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'credit_card',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'current_liability',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'tax_payable',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'current_liability',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'other_current_liability',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'current_liability',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'non_current_liability',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'current_liability',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'long_term_liability',
+ normal: 'credit',
+ root_type: 'liability',
+ child_type: 'long_term_liability',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'equity',
+ normal: 'credit',
+ root_type: 'equity',
+ child_type: 'equity',
+ balance_sheet: true,
+ income_sheet: false,
+ },
+ {
+ key: 'income',
+ normal: 'credit',
+ root_type: 'income',
+ child_type: 'income',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'other_income',
+ normal: 'credit',
+ root_type: 'income',
+ child_type: 'other_income',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'cost_of_goods_sold',
+ normal: 'debit',
+ root_type: 'expenses',
+ child_type: 'expenses',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'expense',
+ normal: 'debit',
+ root_type: 'expense',
+ child_type: 'expense',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+ {
+ key: 'other_expense',
+ normal: 'debit',
+ root_type: 'expense',
+ balance_sheet: false,
+ income_sheet: true,
+ },
+];
\ No newline at end of file
diff --git a/server/src/interfaces/BalanceSheet.ts b/server/src/interfaces/BalanceSheet.ts
index f9a8e631d..cf330b1dc 100644
--- a/server/src/interfaces/BalanceSheet.ts
+++ b/server/src/interfaces/BalanceSheet.ts
@@ -42,7 +42,7 @@ export interface IBalanceSheetStructureSection {
sectionType?: string;
type: 'section' | 'accounts_section';
children?: IBalanceSheetStructureSection[];
- accountsTypesRelated?: string[];
+ accountsTypes?: string[];
alwaysShow?: boolean;
}
@@ -74,6 +74,6 @@ export interface IBalanceSheetSection {
total: IBalanceSheetAccountTotal;
totalPeriods?: IBalanceSheetAccountTotal[];
- accountsTypesRelated?: string[];
+ accountsTypes?: string[];
_forceShow?: boolean;
}
diff --git a/server/src/interfaces/GeneralLedgerSheet.ts b/server/src/interfaces/GeneralLedgerSheet.ts
index 5c75a1137..84d0cc607 100644
--- a/server/src/interfaces/GeneralLedgerSheet.ts
+++ b/server/src/interfaces/GeneralLedgerSheet.ts
@@ -14,13 +14,26 @@ export interface IGeneralLedgerSheetQuery {
export interface IGeneralLedgerSheetAccountTransaction {
id: number,
+
amount: number,
+ runningBalance: number,
+ credit: number,
+ debit: number,
+
formattedAmount: string,
+ formattedCredit: string,
+ formattedDebit: string,
+ formattedRunningBalance: string,
+
currencyCode: string,
note?: string,
+
transactionType?: string,
+ transactionNumber: string,
+
referenceId?: number,
referenceType?: string,
+
date: Date|string,
};
@@ -38,8 +51,8 @@ export interface IGeneralLedgerSheetAccount {
index: number,
parentAccountId: number,
transactions: IGeneralLedgerSheetAccountTransaction[],
- opening: IGeneralLedgerSheetAccountBalance,
- closing: IGeneralLedgerSheetAccountBalance,
+ openingBalance: IGeneralLedgerSheetAccountBalance,
+ closingBalance: IGeneralLedgerSheetAccountBalance,
}
export interface IAccountTransaction {
diff --git a/server/src/interfaces/ProfitLossSheet.ts b/server/src/interfaces/ProfitLossSheet.ts
index da0b81059..f070c1f1b 100644
--- a/server/src/interfaces/ProfitLossSheet.ts
+++ b/server/src/interfaces/ProfitLossSheet.ts
@@ -50,7 +50,7 @@ export interface IProfitLossSheetStatement {
costOfSales: IProfitLossSheetAccountsSection,
expenses: IProfitLossSheetAccountsSection,
otherExpenses: IProfitLossSheetAccountsSection,
-
+ otherIncome: IProfitLossSheetAccountsSection,
netIncome: IProfitLossSheetTotalSection;
operatingProfit: IProfitLossSheetTotalSection;
grossProfit: IProfitLossSheetTotalSection;
diff --git a/server/src/interfaces/User.ts b/server/src/interfaces/User.ts
index 4389c0613..162d1b879 100644
--- a/server/src/interfaces/User.ts
+++ b/server/src/interfaces/User.ts
@@ -1,6 +1,6 @@
+import { Model } from 'objection';
-
-export interface ISystemUser {
+export interface ISystemUser extends Model {
id: number,
firstName: string,
lastName: string,
@@ -34,4 +34,12 @@ export interface IInviteUserInput {
lastName: string,
phoneNumber: string,
password: string,
+};
+
+export interface IUserInvite {
+ id: number,
+ email: string,
+ token: string,
+ tenantId: number,
+ createdAt?: Date,
}
\ No newline at end of file
diff --git a/server/src/models/AccountType.js b/server/src/models/AccountType.js
index b57c17ec8..8d16b1dee 100644
--- a/server/src/models/AccountType.js
+++ b/server/src/models/AccountType.js
@@ -57,20 +57,24 @@ export default class AccountType extends TenantModel {
*/
static get labels() {
return {
- fixed_asset: 'Fixed asset',
- current_asset: "Current asset",
- long_term_liability: "Long term liability",
- current_liability: "Current liability",
+ inventory: 'Inventory',
+ other_current_asset: 'Other Current Asset',
+ bank: 'Bank Account',
+ cash: 'Cash',
+ fixed_asset: 'Fixed Asset',
+ non_current_asset: 'Non-Current Asset',
+ accounts_payable: 'Accounts Payable (A/P)',
+ accounts_receivable: 'Accounts Receivable (A/R)',
+ credit_card: 'Credit Card',
+ long_term_liability: 'Long Term Liability',
+ other_current_liability: 'Other Current Liability',
+ other_liability: 'Other Liability',
equity: "Equity",
expense: "Expense",
income: "Income",
- accounts_receivable: "Accounts receivable",
- accounts_payable: "Accounts payable",
- other_expense: "Other expense",
- other_income: "Other income",
- cost_of_goods_sold: "Cost of goods sold (COGS)",
- other_liability: "Other liability",
- other_asset: 'Other asset',
+ other_income: "Other Income",
+ other_expense: "Other Expense",
+ cost_of_goods_sold: "Cost of Goods Sold (COGS)",
};
}
}
diff --git a/server/src/repositories/EntityRepository.ts b/server/src/repositories/EntityRepository.ts
index e948d6068..75705998c 100644
--- a/server/src/repositories/EntityRepository.ts
+++ b/server/src/repositories/EntityRepository.ts
@@ -1,4 +1,4 @@
-import { cloneDeep, cloneDeepWith, forOwn, isString } from 'lodash';
+import { cloneDeep, forOwn, isString } from 'lodash';
import ModelEntityNotFound from 'exceptions/ModelEntityNotFound';
export default class EntityRepository {
@@ -38,8 +38,7 @@ export default class EntityRepository {
* @returns {Promise