mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
WIP Multi-tenant architecture.
This commit is contained in:
25
server/config/config.js
Normal file
25
server/config/config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
module.exports = {
|
||||||
|
system: {
|
||||||
|
db_client: 'mysql',
|
||||||
|
db_host: '127.0.0.1',
|
||||||
|
db_user: 'root',
|
||||||
|
db_password: '123123123',
|
||||||
|
db_name: 'bigcapital_system',
|
||||||
|
migrations_dir: '../src/system/migrations',
|
||||||
|
},
|
||||||
|
tenant: {
|
||||||
|
db_client: 'mysql',
|
||||||
|
db_name_prefix: 'bigcapital_tenant_',
|
||||||
|
db_host: '127.0.0.1',
|
||||||
|
db_user: 'root',
|
||||||
|
db_password: '123123123',
|
||||||
|
charset: 'utf8',
|
||||||
|
migrations_dir: 'src/database/migrations',
|
||||||
|
seeds_dir: 'src/database/seeds',
|
||||||
|
},
|
||||||
|
manager: {
|
||||||
|
superUser: 'root',
|
||||||
|
superPassword: '123123123',
|
||||||
|
},
|
||||||
|
};
|
||||||
20
server/config/systemKnexfile.js
Normal file
20
server/config/systemKnexfile.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
const configEnv = {
|
||||||
|
client: config.system.db_client,
|
||||||
|
connection: {
|
||||||
|
host: config.system.db_host,
|
||||||
|
user: config.system.db_user,
|
||||||
|
password: config.system.db_password,
|
||||||
|
database: config.system.db_name,
|
||||||
|
charset: 'utf8',
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: config.system.migrations_dir,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
development: configEnv,
|
||||||
|
production: configEnv,
|
||||||
|
};
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
const MIGRATIONS_DIR = './src/database/migrations';
|
|
||||||
const SEEDS_DIR = './src/database/seeds';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
test: {
|
|
||||||
client: process.env.DB_CLIENT,
|
|
||||||
migrations: {
|
|
||||||
directory: MIGRATIONS_DIR,
|
|
||||||
},
|
|
||||||
connection: {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
charset: 'utf8',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
development: {
|
|
||||||
client: process.env.DB_CLIENT,
|
|
||||||
connection: {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
charset: 'utf8',
|
|
||||||
},
|
|
||||||
migrations: {
|
|
||||||
directory: MIGRATIONS_DIR,
|
|
||||||
},
|
|
||||||
seeds: {
|
|
||||||
directory: SEEDS_DIR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
client: process.env.DB_CLIENT,
|
|
||||||
connection: {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
charset: 'utf8',
|
|
||||||
},
|
|
||||||
migrations: {
|
|
||||||
directory: MIGRATIONS_DIR,
|
|
||||||
},
|
|
||||||
seeds: {
|
|
||||||
directory: SEEDS_DIR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"i18n": "^0.8.5",
|
"i18n": "^0.8.5",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.20.3",
|
"knex": "^0.20.3",
|
||||||
|
"knex-cleaner": "^1.3.0",
|
||||||
"knex-db-manager": "^0.6.1",
|
"knex-db-manager": "^0.6.1",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
@@ -43,7 +44,9 @@
|
|||||||
"node-cache": "^4.2.1",
|
"node-cache": "^4.2.1",
|
||||||
"nodemailer": "^6.3.0",
|
"nodemailer": "^6.3.0",
|
||||||
"nodemon": "^1.19.1",
|
"nodemon": "^1.19.1",
|
||||||
"objection": "^2.0.10"
|
"objection": "^2.0.10",
|
||||||
|
"uniqid": "^5.2.0",
|
||||||
|
"winston": "^3.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.5",
|
"@babel/core": "^7.5.5",
|
||||||
@@ -56,6 +59,7 @@
|
|||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"chai-http": "^4.3.0",
|
"chai-http": "^4.3.0",
|
||||||
"chai-things": "^0.2.0",
|
"chai-things": "^0.2.0",
|
||||||
|
"commander": "^5.0.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"eslint": "^6.2.1",
|
"eslint": "^6.2.1",
|
||||||
"eslint-config-airbnb-base": "^14.0.0",
|
"eslint-config-airbnb-base": "^14.0.0",
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import helmet from 'helmet';
|
|||||||
import boom from 'express-boom';
|
import boom from 'express-boom';
|
||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import '../config';
|
import '../config';
|
||||||
|
import '@/database/objection';
|
||||||
import routes from '@/http';
|
import routes from '@/http';
|
||||||
import '@/models';
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Knex from 'knex';
|
import Knex from 'knex';
|
||||||
import { knexSnakeCaseMappers } from 'objection';
|
import { knexSnakeCaseMappers } from 'objection';
|
||||||
import knexfile from '@/../knexfile';
|
import knexfile from '@/../config/systemKnexfile';
|
||||||
|
|
||||||
const config = knexfile[process.env.NODE_ENV];
|
const config = knexfile[process.env.NODE_ENV];
|
||||||
const knex = Knex({
|
const knex = Knex({
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import knexManager from 'knex-db-manager';
|
import knexManager from 'knex-db-manager';
|
||||||
import knexfile from '@/../knexfile';
|
import knexfile from '@/../config/systemKnexfile';
|
||||||
|
import config from '@/../config/config';
|
||||||
|
|
||||||
const config = knexfile[process.env.NODE_ENV];
|
const knexConfig = knexfile[process.env.NODE_ENV];
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
superUser: config.manager.superUser,
|
||||||
|
superPassword: config.manager.superPassword,
|
||||||
|
});
|
||||||
const dbManager = knexManager.databaseManagerFactory({
|
const dbManager = knexManager.databaseManagerFactory({
|
||||||
knex: config,
|
knex: knexConfig,
|
||||||
dbManager: {
|
dbManager: {
|
||||||
// db manager related configuration
|
|
||||||
collate: [],
|
collate: [],
|
||||||
superUser: 'root',
|
superUser: config.manager.superUser,
|
||||||
superPassword: 'root',
|
superPassword: config.manager.superPassword,
|
||||||
// populatePathPattern: 'data/**/*.js', // glob format for searching seeds
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,6 @@ exports.up = function (knex) {
|
|||||||
table.string('language');
|
table.string('language');
|
||||||
table.date('last_login_at');
|
table.date('last_login_at');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).then(() => {
|
|
||||||
knex.seed.run({
|
|
||||||
specific: 'seed_users.js',
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import express from 'express';
|
|||||||
import { check, validationResult, oneOf } from 'express-validator';
|
import { check, validationResult, oneOf } from 'express-validator';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import asyncMiddleware from '../middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import Account from '@/models/Account';
|
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||||
import ManualJournal from '@/models/ManualJournal';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -57,11 +55,13 @@ export default {
|
|||||||
const date = moment(form.date).format('YYYY-MM-DD');
|
const date = moment(form.date).format('YYYY-MM-DD');
|
||||||
|
|
||||||
const accountsIds = accounts.map((account) => account.id);
|
const accountsIds = accounts.map((account) => account.id);
|
||||||
|
|
||||||
|
const { Account, ManualJournal } = req.models;
|
||||||
const storedAccounts = await Account.query()
|
const storedAccounts = await Account.query()
|
||||||
.select(['id']).whereIn('id', accountsIds)
|
.select(['id']).whereIn('id', accountsIds)
|
||||||
.withGraphFetched('type');
|
.withGraphFetched('type');
|
||||||
|
|
||||||
const accountsCollection = new Map(storedAccounts.map(i => [i.id, i]));
|
const accountsCollection = new Map(storedAccounts.map((i) => [i.id, i]));
|
||||||
|
|
||||||
// Get the stored accounts Ids and difference with submit accounts.
|
// Get the stored accounts Ids and difference with submit accounts.
|
||||||
const accountsStoredIds = storedAccounts.map((account) => account.id);
|
const accountsStoredIds = storedAccounts.map((account) => account.id);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import AccountType from '@/models/AccountType';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +23,7 @@ export default {
|
|||||||
getAccountTypesList: {
|
getAccountTypesList: {
|
||||||
validation: [],
|
validation: [],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const { AccountType } = req.models;
|
||||||
const accountTypes = await AccountType.query();
|
const accountTypes = await AccountType.query();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import { check, query, oneOf, validationResult, param } from 'express-validator';
|
import { check, query, validationResult, param } from 'express-validator';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Account from '@/models/Account';
|
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||||
import ManualJournal from '@/models/JournalEntry';
|
|
||||||
import AccountTransaction from '@/models/AccountTransaction';
|
|
||||||
import Resource from '@/models/Resource';
|
|
||||||
import View from '@/models/View';
|
|
||||||
import {
|
import {
|
||||||
mapViewRolesToConditionals,
|
mapViewRolesToConditionals,
|
||||||
mapFilterRolesToDynamicFilter,
|
mapFilterRolesToDynamicFilter,
|
||||||
@@ -55,6 +50,10 @@ export default {
|
|||||||
this.deleteManualJournal.validation,
|
this.deleteManualJournal.validation,
|
||||||
asyncMiddleware(this.deleteManualJournal.handler));
|
asyncMiddleware(this.deleteManualJournal.handler));
|
||||||
|
|
||||||
|
router.delete('/manual-journals',
|
||||||
|
this.deleteBulkManualJournals.validation,
|
||||||
|
asyncMiddleware(this.deleteBulkManualJournals.handler));
|
||||||
|
|
||||||
router.post('/recurring-journal-entries',
|
router.post('/recurring-journal-entries',
|
||||||
this.recurringJournalEntries.validation,
|
this.recurringJournalEntries.validation,
|
||||||
asyncMiddleware(this.recurringJournalEntries.handler));
|
asyncMiddleware(this.recurringJournalEntries.handler));
|
||||||
@@ -91,6 +90,7 @@ export default {
|
|||||||
if (filter.stringified_filter_roles) {
|
if (filter.stringified_filter_roles) {
|
||||||
filter.filter_roles = JSON.parse(filter.stringified_filter_roles);
|
filter.filter_roles = JSON.parse(filter.stringified_filter_roles);
|
||||||
}
|
}
|
||||||
|
const { Resource, View, ManualJournal } = req.models;
|
||||||
|
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
const manualJournalsResource = await Resource.query()
|
const manualJournalsResource = await Resource.query()
|
||||||
@@ -200,6 +200,8 @@ export default {
|
|||||||
reference: '',
|
reference: '',
|
||||||
...req.body,
|
...req.body,
|
||||||
};
|
};
|
||||||
|
const { ManualJournal, Account } = req.models;
|
||||||
|
|
||||||
let totalCredit = 0;
|
let totalCredit = 0;
|
||||||
let totalDebit = 0;
|
let totalDebit = 0;
|
||||||
|
|
||||||
@@ -341,6 +343,10 @@ export default {
|
|||||||
...req.body,
|
...req.body,
|
||||||
};
|
};
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const {
|
||||||
|
ManualJournal, AccountTransaction, Account,
|
||||||
|
} = req.models;
|
||||||
|
|
||||||
const manualJournal = await ManualJournal.query().where('id', id).first();
|
const manualJournal = await ManualJournal.query().where('id', id).first();
|
||||||
|
|
||||||
if (!manualJournal) {
|
if (!manualJournal) {
|
||||||
@@ -456,6 +462,11 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
ManualJournal,
|
||||||
|
AccountTransaction,
|
||||||
|
} = req.models;
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const manualJournal = await ManualJournal.query()
|
const manualJournal = await ManualJournal.query()
|
||||||
.where('id', id).first();
|
.where('id', id).first();
|
||||||
@@ -508,6 +519,10 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
ManualJournal, AccountTransaction,
|
||||||
|
} = req.models;
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const manualJournal = await ManualJournal.query()
|
const manualJournal = await ManualJournal.query()
|
||||||
.where('id', id).first();
|
.where('id', id).first();
|
||||||
@@ -549,6 +564,9 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const {
|
||||||
|
ManualJournal, AccountTransaction,
|
||||||
|
} = req.models;
|
||||||
const manualJournal = await ManualJournal.query()
|
const manualJournal = await ManualJournal.query()
|
||||||
.where('id', id).first();
|
.where('id', id).first();
|
||||||
|
|
||||||
@@ -614,6 +632,7 @@ export default {
|
|||||||
}
|
}
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
|
const { Account } = req.models;
|
||||||
|
|
||||||
const foundAccounts = await Account.query()
|
const foundAccounts = await Account.query()
|
||||||
.where('id', form.credit_account_id)
|
.where('id', form.credit_account_id)
|
||||||
@@ -642,4 +661,54 @@ export default {
|
|||||||
return res.status(200).send();
|
return res.status(200).send();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes bulk manual journals.
|
||||||
|
*/
|
||||||
|
deleteBulkManualJournals: {
|
||||||
|
validation: [
|
||||||
|
query('ids').isArray({ min: 2 }),
|
||||||
|
query('ids.*').isNumeric().toInt(),
|
||||||
|
],
|
||||||
|
async handler(req, res) {
|
||||||
|
const validationErrors = validationResult(req);
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return res.boom.badData(null, {
|
||||||
|
code: 'validation_error', ...validationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const filter = { ...req.query };
|
||||||
|
const { ManualJournal, AccountTransaction } = req.models;
|
||||||
|
|
||||||
|
const manualJournals = await ManualJournal.query()
|
||||||
|
.whereIn('id', filter.ids);
|
||||||
|
|
||||||
|
const notFoundManualJournals = difference(filter.ids, manualJournals.map(m => m.id));
|
||||||
|
|
||||||
|
if (notFoundManualJournals.length > 0) {
|
||||||
|
return res.status(404).send({
|
||||||
|
errors: [{ type: 'MANUAL.JOURNAL.NOT.FOUND', code: 200 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const transactions = await AccountTransaction.query()
|
||||||
|
.whereIn('reference_type', ['Journal', 'ManualJournal'])
|
||||||
|
.whereIn('reference_id', filter.ids);
|
||||||
|
|
||||||
|
const journal = new JournalPoster();
|
||||||
|
|
||||||
|
journal.loadEntries(transactions);
|
||||||
|
journal.removeEntries();
|
||||||
|
|
||||||
|
await ManualJournal.query()
|
||||||
|
.whereIn('id', filter.ids).delete();
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
journal.deleteEntries(),
|
||||||
|
journal.saveBalance(),
|
||||||
|
]);
|
||||||
|
return res.status(200).send({ ids: filter.ids });
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,15 +7,10 @@ import {
|
|||||||
} from 'express-validator';
|
} from 'express-validator';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import Account from '@/models/Account';
|
|
||||||
import AccountType from '@/models/AccountType';
|
|
||||||
import AccountTransaction from '@/models/AccountTransaction';
|
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import AccountBalance from '@/models/AccountBalance';
|
|
||||||
import NestedSet from '@/collection/NestedSet';
|
import NestedSet from '@/collection/NestedSet';
|
||||||
import Resource from '@/models/Resource';
|
|
||||||
import View from '@/models/View';
|
|
||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
|
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||||
import {
|
import {
|
||||||
mapViewRolesToConditionals,
|
mapViewRolesToConditionals,
|
||||||
mapFilterRolesToDynamicFilter,
|
mapFilterRolesToDynamicFilter,
|
||||||
@@ -36,6 +31,8 @@ export default {
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.use(JWTAuth);
|
router.use(JWTAuth);
|
||||||
|
router.use(TenancyMiddleware);
|
||||||
|
|
||||||
router.post('/',
|
router.post('/',
|
||||||
this.newAccount.validation,
|
this.newAccount.validation,
|
||||||
asyncMiddleware(this.newAccount.handler));
|
asyncMiddleware(this.newAccount.handler));
|
||||||
@@ -84,8 +81,12 @@ export default {
|
|||||||
*/
|
*/
|
||||||
newAccount: {
|
newAccount: {
|
||||||
validation: [
|
validation: [
|
||||||
check('name').exists().isLength({ min: 3 }).trim().escape(),
|
check('name').exists().isLength({ min: 3 })
|
||||||
check('code').optional().isLength({ max: 10 }).trim().escape(),
|
.trim()
|
||||||
|
.escape(),
|
||||||
|
check('code').optional().isLength({ max: 10 })
|
||||||
|
.trim()
|
||||||
|
.escape(),
|
||||||
check('account_type_id').exists().isNumeric().toInt(),
|
check('account_type_id').exists().isNumeric().toInt(),
|
||||||
check('description').optional().trim().escape(),
|
check('description').optional().trim().escape(),
|
||||||
],
|
],
|
||||||
@@ -98,6 +99,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
|
const { AccountType, Account } = req.models;
|
||||||
|
|
||||||
const foundAccountCodePromise = form.code
|
const foundAccountCodePromise = form.code
|
||||||
? Account.query().where('code', form.code) : null;
|
? Account.query().where('code', form.code) : null;
|
||||||
@@ -131,8 +133,12 @@ export default {
|
|||||||
editAccount: {
|
editAccount: {
|
||||||
validation: [
|
validation: [
|
||||||
param('id').exists().toInt(),
|
param('id').exists().toInt(),
|
||||||
check('name').exists().isLength({ min: 3 }).trim().escape(),
|
check('name').exists().isLength({ min: 3 })
|
||||||
check('code').exists().isLength({ max: 10 }).trim().escape(),
|
.trim()
|
||||||
|
.escape(),
|
||||||
|
check('code').exists().isLength({ max: 10 })
|
||||||
|
.trim()
|
||||||
|
.escape(),
|
||||||
check('account_type_id').exists().isNumeric().toInt(),
|
check('account_type_id').exists().isNumeric().toInt(),
|
||||||
check('description').optional().trim().escape(),
|
check('description').optional().trim().escape(),
|
||||||
],
|
],
|
||||||
@@ -145,6 +151,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Account, AccountType } = req.models;
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
const account = await Account.query().findById(id);
|
const account = await Account.query().findById(id);
|
||||||
|
|
||||||
@@ -185,6 +192,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Account } = req.models;
|
||||||
const account = await Account.query().where('id', id).first();
|
const account = await Account.query().where('id', id).first();
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -203,6 +211,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Account, AccountTransaction } = req.models;
|
||||||
const account = await Account.query().findById(id);
|
const account = await Account.query().findById(id);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -255,6 +264,8 @@ export default {
|
|||||||
if (filter.stringified_filter_roles) {
|
if (filter.stringified_filter_roles) {
|
||||||
filter.filter_roles = JSON.parse(filter.stringified_filter_roles);
|
filter.filter_roles = JSON.parse(filter.stringified_filter_roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { Resource, Account, View } = req.models;
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
const accountsResource = await Resource.query()
|
const accountsResource = await Resource.query()
|
||||||
@@ -294,7 +305,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// View roles.
|
// View roles.
|
||||||
if (view && view.roles.length > 0) {
|
if (view && view.roles.length > 0) {
|
||||||
const viewFilter = new DynamicFilterViews(
|
const viewFilter = new DynamicFilterViews(
|
||||||
mapViewRolesToConditionals(view.roles),
|
mapViewRolesToConditionals(view.roles),
|
||||||
view.rolesLogicExpression,
|
view.rolesLogicExpression,
|
||||||
@@ -349,6 +360,11 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const {
|
||||||
|
Account,
|
||||||
|
AccountTransaction,
|
||||||
|
AccountBalance,
|
||||||
|
} = req.models;
|
||||||
const account = await Account.findById(id);
|
const account = await Account.findById(id);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -381,6 +397,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Account } = req.models;
|
||||||
const account = await Account.findById(id);
|
const account = await Account.findById(id);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -403,6 +420,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Account } = req.models;
|
||||||
const account = await Account.findById(id);
|
const account = await Account.findById(id);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -461,12 +479,14 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const filter = { ids: [], ...req.query };
|
const filter = { ids: [], ...req.query };
|
||||||
|
const { Account, AccountTransaction } = req.models;
|
||||||
|
|
||||||
const accounts = await Account.query().onBuild((builder) => {
|
const accounts = await Account.query().onBuild((builder) => {
|
||||||
if (filter.ids.length) {
|
if (filter.ids.length) {
|
||||||
builder.whereIn('id', filter.ids);
|
builder.whereIn('id', filter.ids);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const accountsIds = accounts.map(a => a.id);
|
const accountsIds = accounts.map((a) => a.id);
|
||||||
const notFoundAccounts = difference(filter.ids, accountsIds);
|
const notFoundAccounts = difference(filter.ids, accountsIds);
|
||||||
|
|
||||||
if (notFoundAccounts.length > 0) {
|
if (notFoundAccounts.length > 0) {
|
||||||
|
|||||||
@@ -5,11 +5,18 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import Mustache from 'mustache';
|
import Mustache from 'mustache';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import asyncMiddleware from '../middleware/asyncMiddleware';
|
import { pick } from 'lodash';
|
||||||
import User from '@/models/User';
|
import uniqid from 'uniqid';
|
||||||
import PasswordReset from '@/models/PasswordReset';
|
import Logger from '@/services/Logger';
|
||||||
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
|
import SystemUser from '@/system/models/SystemUser';
|
||||||
import mail from '@/services/mail';
|
import mail from '@/services/mail';
|
||||||
import { hashPassword } from '@/utils';
|
import { hashPassword } from '@/utils';
|
||||||
|
import dbManager from '@/database/manager';
|
||||||
|
import Tenant from '@/system/models/Tenant';
|
||||||
|
import TenantUser from '@/models/TenantUser';
|
||||||
|
import TenantsManager from '@/system/TenantsManager';
|
||||||
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -22,6 +29,10 @@ export default {
|
|||||||
this.login.validation,
|
this.login.validation,
|
||||||
asyncMiddleware(this.login.handler));
|
asyncMiddleware(this.login.handler));
|
||||||
|
|
||||||
|
router.post('/register',
|
||||||
|
this.register.validation,
|
||||||
|
asyncMiddleware(this.register.handler));
|
||||||
|
|
||||||
router.post('/send_reset_password',
|
router.post('/send_reset_password',
|
||||||
this.sendResetPassword.validation,
|
this.sendResetPassword.validation,
|
||||||
asyncMiddleware(this.sendResetPassword.handler));
|
asyncMiddleware(this.sendResetPassword.handler));
|
||||||
@@ -49,12 +60,15 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { crediential, password } = req.body;
|
const form = { ...req.body };
|
||||||
const { JWT_SECRET_KEY } = process.env;
|
const { JWT_SECRET_KEY } = process.env;
|
||||||
|
|
||||||
const user = await User.query()
|
Logger.log('info', 'Someone trying to login.', { form });
|
||||||
.where('email', crediential)
|
|
||||||
.orWhere('phone_number', crediential)
|
const user = await SystemUser.query()
|
||||||
|
.withGraphFetched('tenant')
|
||||||
|
.where('email', form.crediential)
|
||||||
|
.orWhere('phone_number', form.crediential)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -62,7 +76,7 @@ export default {
|
|||||||
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!user.verifyPassword(password)) {
|
if (!user.verifyPassword(form.password)) {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
||||||
});
|
});
|
||||||
@@ -74,16 +88,89 @@ export default {
|
|||||||
}
|
}
|
||||||
// user.update({ last_login_at: new Date() });
|
// user.update({ last_login_at: new Date() });
|
||||||
|
|
||||||
const token = jwt.sign({
|
const token = jwt.sign(
|
||||||
email: user.email,
|
{ email: user.email, _id: user.id },
|
||||||
_id: user.id,
|
JWT_SECRET_KEY,
|
||||||
}, JWT_SECRET_KEY, {
|
{ expiresIn: '1d' },
|
||||||
expiresIn: '1d',
|
);
|
||||||
});
|
Logger.log('info', 'Logging success.', { form });
|
||||||
|
|
||||||
return res.status(200).send({ token, user });
|
return res.status(200).send({ token, user });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new organization.
|
||||||
|
*/
|
||||||
|
register: {
|
||||||
|
validation: [
|
||||||
|
check('organization_name').exists().trim().escape(),
|
||||||
|
check('first_name').exists().trim().escape(),
|
||||||
|
check('last_name').exists().trim().escape(),
|
||||||
|
check('email').exists().trim().escape(),
|
||||||
|
check('phone_number').exists().trim().escape(),
|
||||||
|
check('password').exists().trim().escape(),
|
||||||
|
check('country').exists().trim().escape(),
|
||||||
|
],
|
||||||
|
async handler(req, res) {
|
||||||
|
const validationErrors = validationResult(req);
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return res.boom.badData(null, {
|
||||||
|
code: 'validation_error', ...validationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const form = { ...req.body };
|
||||||
|
Logger.log('info', 'Someone trying to register.', { form });
|
||||||
|
|
||||||
|
const user = await SystemUser.query()
|
||||||
|
.where('email', form.email)
|
||||||
|
.orWhere('phone_number', form.phone_number)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (user && user.phoneNumber === form.phone_number) {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'PHONE_NUMBER_EXISTS', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (user && user.email === form.email) {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'EMAIL_EXISTS', code: 200 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const organizationId = uniqid();
|
||||||
|
const tenantOrganization = await Tenant.query().insert({
|
||||||
|
organization_id: organizationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hashedPassword = await hashPassword(form.password);
|
||||||
|
const userInsert = {
|
||||||
|
...pick(form, ['first_name', 'last_name', 'email', 'phone_number']),
|
||||||
|
password: hashedPassword,
|
||||||
|
active: true,
|
||||||
|
};
|
||||||
|
const registeredUser = await SystemUser.query().insert({
|
||||||
|
...userInsert,
|
||||||
|
tenant_id: tenantOrganization.id,
|
||||||
|
});
|
||||||
|
await dbManager.createDb(`bigcapital_tenant_${organizationId}`);
|
||||||
|
|
||||||
|
const tenantDb = TenantsManager.knexInstance(organizationId);
|
||||||
|
await tenantDb.migrate.latest();
|
||||||
|
|
||||||
|
TenantModel.knexBinded = tenantDb;
|
||||||
|
|
||||||
|
await TenantUser.bindKnex(tenantDb).query().insert({
|
||||||
|
...userInsert,
|
||||||
|
});
|
||||||
|
Logger.log('info', 'New tenant has been created.', { organizationId });
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
organization_id: organizationId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send reset password link via email or SMS.
|
* Send reset password link via email or SMS.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { check, param, validationResult } from 'express-validator';
|
import { check, param, validationResult } from 'express-validator';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import Currency from '@/models/Currency';
|
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -17,7 +16,7 @@ export default {
|
|||||||
router.post('/',
|
router.post('/',
|
||||||
this.newCurrency.validation,
|
this.newCurrency.validation,
|
||||||
asyncMiddleware(this.newCurrency.handler));
|
asyncMiddleware(this.newCurrency.handler));
|
||||||
|
|
||||||
router.post('/:id',
|
router.post('/:id',
|
||||||
this.editCurrency.validation,
|
this.editCurrency.validation,
|
||||||
asyncMiddleware(this.editCurrency.handler));
|
asyncMiddleware(this.editCurrency.handler));
|
||||||
@@ -35,6 +34,7 @@ export default {
|
|||||||
all: {
|
all: {
|
||||||
validation: [],
|
validation: [],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const { Currency } = req.models;
|
||||||
const currencies = await Currency.query();
|
const currencies = await Currency.query();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
@@ -59,6 +59,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
|
const { Currency } = req.models;
|
||||||
|
|
||||||
const foundCurrency = await Currency.query()
|
const foundCurrency = await Currency.query()
|
||||||
.where('currency_code', form.currency_code);
|
.where('currency_code', form.currency_code);
|
||||||
@@ -89,6 +90,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Currency } = req.models;
|
||||||
const { currency_code: currencyCode } = req.params;
|
const { currency_code: currencyCode } = req.params;
|
||||||
|
|
||||||
await Currency.query()
|
await Currency.query()
|
||||||
@@ -115,23 +117,19 @@ export default {
|
|||||||
}
|
}
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Currency } = req.models;
|
||||||
|
|
||||||
const foundCurrency = await Currency.query()
|
const foundCurrency = await Currency.query()
|
||||||
.where('currency_code', form.currency_code)
|
.where('currency_code', form.currency_code).whereNot('id', id);
|
||||||
.whereNot('id', id);
|
|
||||||
|
|
||||||
if (foundCurrency.length > 0) {
|
if (foundCurrency.length > 0) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100 }],
|
errors: [{ type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await Currency.query()
|
await Currency.query().where('id', id).update({ ...form });
|
||||||
.where('id', id)
|
|
||||||
.update({ ...form });
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({ currency: { ...form } });
|
||||||
currency: { ...form },
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { check, param, query, validationResult } from 'express-validator';
|
import {
|
||||||
|
check,
|
||||||
|
param,
|
||||||
|
query,
|
||||||
|
validationResult,
|
||||||
|
} from 'express-validator';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import ExchangeRate from '@/models/ExchangeRate';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -53,11 +57,12 @@ export default {
|
|||||||
page_size: 10,
|
page_size: 10,
|
||||||
...req.query,
|
...req.query,
|
||||||
};
|
};
|
||||||
|
const { ExchangeRate } = req.models;
|
||||||
const exchangeRates = await ExchangeRate.query()
|
const exchangeRates = await ExchangeRate.query()
|
||||||
.pagination(filter.page - 1, filter.page_size);
|
.pagination(filter.page - 1, filter.page_size);
|
||||||
|
|
||||||
return res.status(200).send({ exchange_rates: exchangeRates });
|
return res.status(200).send({ exchange_rates: exchangeRates });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +82,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { ExchangeRate } = req.models;
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
const foundExchangeRate = await ExchangeRate.query()
|
const foundExchangeRate = await ExchangeRate.query()
|
||||||
.where('currency_code', form.currency_code)
|
.where('currency_code', form.currency_code)
|
||||||
@@ -87,7 +92,7 @@ export default {
|
|||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'EXCHANGE.RATE.DATE.PERIOD.DEFINED', code: 200 }],
|
errors: [{ type: 'EXCHANGE.RATE.DATE.PERIOD.DEFINED', code: 200 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await ExchangeRate.query().insert({
|
await ExchangeRate.query().insert({
|
||||||
...form,
|
...form,
|
||||||
date: moment(form.date).format('YYYY-MM-DD'),
|
date: moment(form.date).format('YYYY-MM-DD'),
|
||||||
@@ -116,6 +121,7 @@ export default {
|
|||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
|
const { ExchangeRate } = req.models;
|
||||||
|
|
||||||
const foundExchangeRate = await ExchangeRate.query()
|
const foundExchangeRate = await ExchangeRate.query()
|
||||||
.where('id', id);
|
.where('id', id);
|
||||||
@@ -148,19 +154,18 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const foundExchangeRate = await ExchangeRate.query()
|
const { ExchangeRate } = req.models;
|
||||||
.where('id', id);
|
const foundExchangeRate = await ExchangeRate.query().where('id', id);
|
||||||
|
|
||||||
if (!foundExchangeRate.length) {
|
if (!foundExchangeRate.length) {
|
||||||
return res.status(404).send({
|
return res.status(404).send({
|
||||||
errors: [{ type: 'EXCHANGE.RATE.NOT.FOUND', code: 200 }],
|
errors: [{ type: 'EXCHANGE.RATE.NOT.FOUND', code: 200 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await ExchangeRate.query()
|
await ExchangeRate.query().where('id', id).delete();
|
||||||
.where('id', id).delete();
|
|
||||||
|
|
||||||
return res.status(200).send({ id });
|
return res.status(200).send({ id });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -8,14 +8,9 @@ import {
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { difference, chain, omit } from 'lodash';
|
import { difference, chain, omit } from 'lodash';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import Expense from '@/models/Expense';
|
|
||||||
import Account from '@/models/Account';
|
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
import AccountTransaction from '@/models/AccountTransaction';
|
|
||||||
import View from '@/models/View';
|
|
||||||
import Resource from '../../models/Resource';
|
|
||||||
import ResourceCustomFieldRepository from '@/services/CustomFields/ResourceCustomFieldRepository';
|
import ResourceCustomFieldRepository from '@/services/CustomFields/ResourceCustomFieldRepository';
|
||||||
import {
|
import {
|
||||||
validateViewRoles,
|
validateViewRoles,
|
||||||
@@ -92,6 +87,7 @@ export default {
|
|||||||
custom_fields: [],
|
custom_fields: [],
|
||||||
...req.body,
|
...req.body,
|
||||||
};
|
};
|
||||||
|
const { Account, Expense } = req.models;
|
||||||
// Convert the date to the general format.
|
// Convert the date to the general format.
|
||||||
form.date = moment(form.date).format('YYYY-MM-DD');
|
form.date = moment(form.date).format('YYYY-MM-DD');
|
||||||
|
|
||||||
@@ -174,6 +170,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Account, Expense } = req.models;
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
@@ -268,6 +265,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Expense, AccountTransaction } = req.models;
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
const expense = await Expense.query().findById(id);
|
const expense = await Expense.query().findById(id);
|
||||||
|
|
||||||
@@ -277,7 +275,6 @@ export default {
|
|||||||
if (errorReasons.length > 0) {
|
if (errorReasons.length > 0) {
|
||||||
return res.status(400).send({ errors: errorReasons });
|
return res.status(400).send({ errors: errorReasons });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expense.published) {
|
if (expense.published) {
|
||||||
errorReasons.push({ type: 'EXPENSE.ALREADY.PUBLISHED', code: 200 });
|
errorReasons.push({ type: 'EXPENSE.ALREADY.PUBLISHED', code: 200 });
|
||||||
}
|
}
|
||||||
@@ -337,6 +334,7 @@ export default {
|
|||||||
page: 1,
|
page: 1,
|
||||||
...req.query,
|
...req.query,
|
||||||
};
|
};
|
||||||
|
const { Resource, View, Expense } = req.models;
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
const expenseResource = await Resource.query().where('name', 'expenses').first();
|
const expenseResource = await Resource.query().where('name', 'expenses').first();
|
||||||
|
|
||||||
@@ -364,7 +362,7 @@ export default {
|
|||||||
viewConditionals = mapViewRolesToConditionals(view.viewRoles);
|
viewConditionals = mapViewRolesToConditionals(view.viewRoles);
|
||||||
|
|
||||||
if (!validateViewRoles(viewConditionals, view.rolesLogicExpression)) {
|
if (!validateViewRoles(viewConditionals, view.rolesLogicExpression)) {
|
||||||
errorReasons.push({ type: 'VIEW.LOGIC.EXPRESSION.INVALID', code: 400 })
|
errorReasons.push({ type: 'VIEW.LOGIC.EXPRESSION.INVALID', code: 400 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!view && filter.custom_view_id) {
|
if (!view && filter.custom_view_id) {
|
||||||
@@ -391,7 +389,7 @@ export default {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
...(view) ? {
|
...(view) ? {
|
||||||
customViewId: view.id,
|
customViewId: view.id,
|
||||||
viewColumns: view.columns,
|
viewColumns: view.columns,
|
||||||
viewConditionals,
|
viewConditionals,
|
||||||
} : {},
|
} : {},
|
||||||
@@ -416,6 +414,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Expense, AccountTransaction } = req.models;
|
||||||
const expenseTransaction = await Expense.query().findById(id);
|
const expenseTransaction = await Expense.query().findById(id);
|
||||||
|
|
||||||
if (!expenseTransaction) {
|
if (!expenseTransaction) {
|
||||||
@@ -463,6 +462,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Expense } = req.models;
|
||||||
const expenseTransaction = await Expense.query().findById(id);
|
const expenseTransaction = await Expense.query().findById(id);
|
||||||
|
|
||||||
if (!expenseTransaction) {
|
if (!expenseTransaction) {
|
||||||
@@ -489,6 +489,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Expense } = req.models;
|
||||||
const expenseTransaction = await Expense.query().findById(id);
|
const expenseTransaction = await Expense.query().findById(id);
|
||||||
|
|
||||||
if (!expenseTransaction) {
|
if (!expenseTransaction) {
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import { query, oneOf, validationResult } from 'express-validator';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { pick, difference, groupBy } from 'lodash';
|
import { pick, difference, groupBy } from 'lodash';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import AccountTransaction from '@/models/AccountTransaction';
|
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import AccountType from '@/models/AccountType';
|
|
||||||
import Account from '@/models/Account';
|
|
||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import { dateRangeCollection } from '@/utils';
|
import { dateRangeCollection } from '@/utils';
|
||||||
|
|
||||||
@@ -89,6 +86,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { AccountTransaction } = req.models;
|
||||||
const filter = {
|
const filter = {
|
||||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
@@ -119,9 +117,9 @@ export default {
|
|||||||
|
|
||||||
const formatNumber = formatNumberClosure(filter.number_format);
|
const formatNumber = formatNumberClosure(filter.number_format);
|
||||||
|
|
||||||
const journalGrouped = groupBy(accountsJournalEntries, (entry) => {
|
const journalGrouped = groupBy(accountsJournalEntries,
|
||||||
return `${entry.referenceId}-${entry.referenceType}`;
|
(entry) => `${entry.referenceId}-${entry.referenceType}`);
|
||||||
});
|
|
||||||
const journal = Object.keys(journalGrouped).map((key) => {
|
const journal = Object.keys(journalGrouped).map((key) => {
|
||||||
const transactionsGroup = journalGrouped[key];
|
const transactionsGroup = journalGrouped[key];
|
||||||
|
|
||||||
@@ -169,6 +167,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { AccountTransaction, Account } = req.models;
|
||||||
const filter = {
|
const filter = {
|
||||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
@@ -289,6 +288,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Account, AccountType } = req.models;
|
||||||
const filter = {
|
const filter = {
|
||||||
display_columns_type: 'total',
|
display_columns_type: 'total',
|
||||||
display_columns_by: '',
|
display_columns_by: '',
|
||||||
@@ -327,22 +327,20 @@ export default {
|
|||||||
filter.from_date, filter.to_date, comparatorDateType,
|
filter.from_date, filter.to_date, comparatorDateType,
|
||||||
) : [];
|
) : [];
|
||||||
|
|
||||||
const totalPeriods = (account) => {
|
const totalPeriods = (account) => ({
|
||||||
// Gets the date range set from start to end date.
|
// Gets the date range set from start to end date.
|
||||||
return {
|
total_periods: dateRangeSet.map((date) => {
|
||||||
total_periods: dateRangeSet.map((date) => {
|
const balance = journalEntries.getClosingBalance(account.id, date, comparatorDateType);
|
||||||
const balance = journalEntries.getClosingBalance(account.id, date, comparatorDateType);
|
return {
|
||||||
return {
|
date,
|
||||||
date,
|
formatted_amount: balanceFormatter(balance),
|
||||||
formatted_amount: balanceFormatter(balance),
|
amount: balance,
|
||||||
amount: balance,
|
};
|
||||||
};
|
}),
|
||||||
}),
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const accountsMapper = (balanceSheetAccounts) => {
|
const accountsMapper = (balanceSheetAccounts) => [
|
||||||
return balanceSheetAccounts.map((account) => {
|
...balanceSheetAccounts.map((account) => {
|
||||||
// Calculates the closing balance to the given date.
|
// Calculates the closing balance to the given date.
|
||||||
const closingBalance = journalEntries.getClosingBalance(account.id, filter.to_date);
|
const closingBalance = journalEntries.getClosingBalance(account.id, filter.to_date);
|
||||||
|
|
||||||
@@ -358,8 +356,8 @@ export default {
|
|||||||
date: filter.to_date,
|
date: filter.to_date,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
}),
|
||||||
};
|
];
|
||||||
// Retrieve all assets accounts.
|
// Retrieve all assets accounts.
|
||||||
const assetsAccounts = accounts.filter((account) => (
|
const assetsAccounts = accounts.filter((account) => (
|
||||||
account.type.normal === 'debit'
|
account.type.normal === 'debit'
|
||||||
@@ -414,6 +412,9 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
Account,
|
||||||
|
} = req.models;
|
||||||
const filter = {
|
const filter = {
|
||||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
@@ -490,6 +491,7 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Account, AccountType } = req.models;
|
||||||
const filter = {
|
const filter = {
|
||||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
|
|||||||
@@ -3,13 +3,6 @@ import { check, query, validationResult } from 'express-validator';
|
|||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import Item from '@/models/Item';
|
|
||||||
import Account from '@/models/Account';
|
|
||||||
import ItemCategory from '@/models/ItemCategory';
|
|
||||||
import Resource from '@/models/Resource';
|
|
||||||
import ResourceField from '@/models/ResourceField';
|
|
||||||
import Authorization from '@/http/middleware/authorization';
|
|
||||||
import View from '@/models/View';
|
|
||||||
import {
|
import {
|
||||||
mapViewRolesToConditionals,
|
mapViewRolesToConditionals,
|
||||||
mapFilterRolesToDynamicFilter,
|
mapFilterRolesToDynamicFilter,
|
||||||
@@ -23,10 +16,11 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* Router constructor.
|
||||||
|
*/
|
||||||
router() {
|
router() {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const permit = Authorization('items');
|
|
||||||
|
|
||||||
router.use(jwtAuth);
|
router.use(jwtAuth);
|
||||||
|
|
||||||
@@ -35,7 +29,6 @@ export default {
|
|||||||
asyncMiddleware(this.editItem.handler));
|
asyncMiddleware(this.editItem.handler));
|
||||||
|
|
||||||
router.post('/',
|
router.post('/',
|
||||||
// permit('create'),
|
|
||||||
this.newItem.validation,
|
this.newItem.validation,
|
||||||
asyncMiddleware(this.newItem.handler));
|
asyncMiddleware(this.newItem.handler));
|
||||||
|
|
||||||
@@ -43,10 +36,6 @@ export default {
|
|||||||
this.deleteItem.validation,
|
this.deleteItem.validation,
|
||||||
asyncMiddleware(this.deleteItem.handler));
|
asyncMiddleware(this.deleteItem.handler));
|
||||||
|
|
||||||
// router.get('/:id',
|
|
||||||
// this.getCategory.validation,
|
|
||||||
// asyncMiddleware(this.getCategory.handler));
|
|
||||||
|
|
||||||
router.get('/',
|
router.get('/',
|
||||||
this.listItems.validation,
|
this.listItems.validation,
|
||||||
asyncMiddleware(this.listItems.handler));
|
asyncMiddleware(this.listItems.handler));
|
||||||
@@ -92,12 +81,19 @@ export default {
|
|||||||
custom_fields: [],
|
custom_fields: [],
|
||||||
...req.body,
|
...req.body,
|
||||||
};
|
};
|
||||||
|
const {
|
||||||
|
Account,
|
||||||
|
Resource,
|
||||||
|
ResourceField,
|
||||||
|
ItemCategory,
|
||||||
|
Item,
|
||||||
|
} = req.models;
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
const costAccountPromise = Account.query().findById(form.cost_account_id);
|
const costAccountPromise = Account.query().findById(form.cost_account_id);
|
||||||
const sellAccountPromise = Account.query().findById(form.sell_account_id);
|
const sellAccountPromise = Account.query().findById(form.sell_account_id);
|
||||||
const inventoryAccountPromise = (form.type === 'inventory') ?
|
const inventoryAccountPromise = (form.type === 'inventory')
|
||||||
Account.query().findByid(form.inventory_account_id) : null;
|
? Account.query().findByid(form.inventory_account_id) : null;
|
||||||
|
|
||||||
const itemCategoryPromise = (form.category_id)
|
const itemCategoryPromise = (form.category_id)
|
||||||
? ItemCategory.query().findById(form.category_id) : null;
|
? ItemCategory.query().findById(form.category_id) : null;
|
||||||
@@ -108,9 +104,9 @@ export default {
|
|||||||
|
|
||||||
// Get resource id than get all resource fields.
|
// Get resource id than get all resource fields.
|
||||||
const resource = await Resource.where('name', 'items').fetch();
|
const resource = await Resource.where('name', 'items').fetch();
|
||||||
const fields = await ResourceField.query((query) => {
|
const fields = await ResourceField.query((builder) => {
|
||||||
query.where('resource_id', resource.id);
|
builder.where('resource_id', resource.id);
|
||||||
query.whereIn('key', customFieldsKeys);
|
builder.whereIn('key', customFieldsKeys);
|
||||||
}).fetchAll();
|
}).fetchAll();
|
||||||
|
|
||||||
const storedFieldsKey = fields.map((f) => f.attributes.key);
|
const storedFieldsKey = fields.map((f) => f.attributes.key);
|
||||||
@@ -186,18 +182,19 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Account, Item, ItemCategory } = req.models;
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
custom_fields: [],
|
custom_fields: [],
|
||||||
...req.body,
|
...req.body,
|
||||||
};
|
};
|
||||||
const item = await Item.query().findById(id);
|
const item = await Item.query().findById(id);
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return res.boom.notFound(null, { errors: [
|
return res.boom.notFound(null, {
|
||||||
{ type: 'ITEM.NOT.FOUND', code: 100 },
|
errors: [{ type: 'ITEM.NOT.FOUND', code: 100 }],
|
||||||
]});
|
});
|
||||||
}
|
}
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
@@ -244,6 +241,7 @@ export default {
|
|||||||
validation: [],
|
validation: [],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { Item } = req.models;
|
||||||
const item = await Item.query().findById(id);
|
const item = await Item.query().findById(id);
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
@@ -251,7 +249,6 @@ export default {
|
|||||||
errors: [{ type: 'ITEM_NOT_FOUND', code: 100 }],
|
errors: [{ type: 'ITEM_NOT_FOUND', code: 100 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the fucking the given item id.
|
// Delete the fucking the given item id.
|
||||||
await Item.query().findById(item.id).delete();
|
await Item.query().findById(item.id).delete();
|
||||||
|
|
||||||
@@ -281,15 +278,16 @@ export default {
|
|||||||
}
|
}
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
const viewConditions = [];
|
const viewConditions = [];
|
||||||
|
const { Resource, Item, View } = req.models;
|
||||||
const itemsResource = await Resource.query()
|
const itemsResource = await Resource.query()
|
||||||
.where('name', 'items')
|
.where('name', 'items')
|
||||||
.withGraphFetched('fields')
|
.withGraphFetched('fields')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!itemsResource) {
|
if (!itemsResource) {
|
||||||
return res.status(400).send({ errors: [
|
return res.status(400).send({
|
||||||
{type: 'ITEMS_RESOURCE_NOT_FOUND', code: 200},
|
errors: [{ type: 'ITEMS_RESOURCE_NOT_FOUND', code: 200 }],
|
||||||
]});
|
});
|
||||||
}
|
}
|
||||||
const filter = {
|
const filter = {
|
||||||
column_sort_order: '',
|
column_sort_order: '',
|
||||||
@@ -377,4 +375,4 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import express from 'express';
|
|||||||
import { body, query, validationResult } from 'express-validator';
|
import { body, query, validationResult } from 'express-validator';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import Option from '@/models/Option';
|
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -43,6 +42,7 @@ export default {
|
|||||||
code: 'VALIDATION_ERROR', ...validationErrors,
|
code: 'VALIDATION_ERROR', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Option } = req.models;
|
||||||
const form = { ...req.body };
|
const form = { ...req.body };
|
||||||
const optionsCollections = await Option.query();
|
const optionsCollections = await Option.query();
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export default {
|
|||||||
errorReasons.push({
|
errorReasons.push({
|
||||||
type: 'OPTIONS.KEY.NOT.DEFINED',
|
type: 'OPTIONS.KEY.NOT.DEFINED',
|
||||||
code: 200,
|
code: 200,
|
||||||
keys: notDefinedOptions.map(o => ({ ...pick(o, ['key', 'group']) })),
|
keys: notDefinedOptions.map((o) => ({ ...pick(o, ['key', 'group']) })),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (errorReasons.length) {
|
if (errorReasons.length) {
|
||||||
@@ -84,6 +84,7 @@ export default {
|
|||||||
code: 'VALIDATION_ERROR', ...validationErrors,
|
code: 'VALIDATION_ERROR', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { Option } = req.models;
|
||||||
const filter = { ...req.query };
|
const filter = { ...req.query };
|
||||||
const options = await Option.query().onBuild((builder) => {
|
const options = await Option.query().onBuild((builder) => {
|
||||||
if (filter.key) {
|
if (filter.key) {
|
||||||
@@ -93,7 +94,6 @@ export default {
|
|||||||
builder.where('group', filter.group);
|
builder.where('group', filter.group);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({ options: options.metadata });
|
return res.status(200).send({ options: options.metadata });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ import express from 'express';
|
|||||||
import {
|
import {
|
||||||
param,
|
param,
|
||||||
query,
|
query,
|
||||||
validationResult,
|
|
||||||
} from 'express-validator';
|
} from 'express-validator';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import Resource from '@/models/Resource';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -37,6 +35,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { resource_slug: resourceSlug } = req.params;
|
const { resource_slug: resourceSlug } = req.params;
|
||||||
|
const { Resource } = req.models;
|
||||||
|
|
||||||
const resource = await Resource.query()
|
const resource = await Resource.query()
|
||||||
.where('name', resourceSlug)
|
.where('name', resourceSlug)
|
||||||
@@ -74,6 +73,7 @@ export default {
|
|||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
const { resource_slug: resourceSlug } = req.params;
|
const { resource_slug: resourceSlug } = req.params;
|
||||||
|
const { Resource } = req.models;
|
||||||
|
|
||||||
const resource = await Resource.query()
|
const resource = await Resource.query()
|
||||||
.where('name', resourceSlug)
|
.where('name', resourceSlug)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { difference, intersection, pick } from 'lodash';
|
import { difference, pick } from 'lodash';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import {
|
import {
|
||||||
check,
|
check,
|
||||||
@@ -9,10 +9,6 @@ import {
|
|||||||
} from 'express-validator';
|
} from 'express-validator';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
import Resource from '@/models/Resource';
|
|
||||||
import View from '@/models/View';
|
|
||||||
import ViewRole from '@/models/ViewRole';
|
|
||||||
import ViewColumn from '@/models/ViewColumn';
|
|
||||||
import {
|
import {
|
||||||
validateViewRoles,
|
validateViewRoles,
|
||||||
} from '@/lib/ViewRolesBuilder';
|
} from '@/lib/ViewRolesBuilder';
|
||||||
@@ -62,6 +58,7 @@ export default {
|
|||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const { Resource, View } = req.models;
|
||||||
const filter = { ...req.query };
|
const filter = { ...req.query };
|
||||||
|
|
||||||
const resource = await Resource.query().onBuild((builder) => {
|
const resource = await Resource.query().onBuild((builder) => {
|
||||||
@@ -113,6 +110,7 @@ export default {
|
|||||||
param('view_id').exists().isNumeric().toInt(),
|
param('view_id').exists().isNumeric().toInt(),
|
||||||
],
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const { View } = req.models;
|
||||||
const { view_id: viewId } = req.params;
|
const { view_id: viewId } = req.params;
|
||||||
const view = await View.query().findById(viewId);
|
const view = await View.query().findById(viewId);
|
||||||
|
|
||||||
@@ -161,6 +159,12 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
Resource,
|
||||||
|
View,
|
||||||
|
ViewColumn,
|
||||||
|
ViewRole,
|
||||||
|
} = req.models;
|
||||||
const form = { roles: [], ...req.body };
|
const form = { roles: [], ...req.body };
|
||||||
const resource = await Resource.query().where('name', form.resource_name).first();
|
const resource = await Resource.query().where('name', form.resource_name).first();
|
||||||
|
|
||||||
@@ -265,6 +269,9 @@ export default {
|
|||||||
code: 'validation_error', ...validationErrors,
|
code: 'validation_error', ...validationErrors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
View, ViewRole, ViewColumn, Resource,
|
||||||
|
} = req.models;
|
||||||
const view = await View.query().where('id', viewId)
|
const view = await View.query().where('id', viewId)
|
||||||
.withGraphFetched('roles.field')
|
.withGraphFetched('roles.field')
|
||||||
.withGraphFetched('columns')
|
.withGraphFetched('columns')
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
// import OAuth2 from '@/http/controllers/OAuth2';
|
// import OAuth2 from '@/http/controllers/OAuth2';
|
||||||
import Authentication from '@/http/controllers/Authentication';
|
import Authentication from '@/http/controllers/Authentication';
|
||||||
import Users from '@/http/controllers/Users';
|
// import Users from '@/http/controllers/Users';
|
||||||
import Roles from '@/http/controllers/Roles';
|
// import Roles from '@/http/controllers/Roles';
|
||||||
import Items from '@/http/controllers/Items';
|
import Items from '@/http/controllers/Items';
|
||||||
import ItemCategories from '@/http/controllers/ItemCategories';
|
import ItemCategories from '@/http/controllers/ItemCategories';
|
||||||
import Accounts from '@/http/controllers/Accounts';
|
import Accounts from '@/http/controllers/Accounts';
|
||||||
import AccountTypes from '@/http/controllers/AccountTypes';
|
import AccountTypes from '@/http/controllers/AccountTypes';
|
||||||
import AccountOpeningBalance from '@/http/controllers/AccountOpeningBalance';
|
// import AccountOpeningBalance from '@/http/controllers/AccountOpeningBalance';
|
||||||
import Views from '@/http/controllers/Views';
|
import Views from '@/http/controllers/Views';
|
||||||
import CustomFields from '@/http/controllers/Fields';
|
// import CustomFields from '@/http/controllers/Fields';
|
||||||
import Accounting from '@/http/controllers/Accounting';
|
import Accounting from '@/http/controllers/Accounting';
|
||||||
import FinancialStatements from '@/http/controllers/FinancialStatements';
|
import FinancialStatements from '@/http/controllers/FinancialStatements';
|
||||||
import Expenses from '@/http/controllers/Expenses';
|
// import Expenses from '@/http/controllers/Expenses';
|
||||||
import Options from '@/http/controllers/Options';
|
import Options from '@/http/controllers/Options';
|
||||||
import Budget from '@/http/controllers/Budget';
|
// import Budget from '@/http/controllers/Budget';
|
||||||
import BudgetReports from '@/http/controllers/BudgetReports';
|
// import BudgetReports from '@/http/controllers/BudgetReports';
|
||||||
import Currencies from '@/http/controllers/Currencies';
|
import Currencies from '@/http/controllers/Currencies';
|
||||||
import Customers from '@/http/controllers/Customers';
|
// import Customers from '@/http/controllers/Customers';
|
||||||
import Suppliers from '@/http/controllers/Suppliers';
|
// import Suppliers from '@/http/controllers/Suppliers';
|
||||||
import Bills from '@/http/controllers/Bills';
|
// import Bills from '@/http/controllers/Bills';
|
||||||
import CurrencyAdjustment from './controllers/CurrencyAdjustment';
|
// import CurrencyAdjustment from './controllers/CurrencyAdjustment';
|
||||||
import Resources from './controllers/Resources';
|
import Resources from './controllers/Resources';
|
||||||
import ExchangeRates from '@/http/controllers/ExchangeRates';
|
import ExchangeRates from '@/http/controllers/ExchangeRates';
|
||||||
// import SalesReports from '@/http/controllers/SalesReports';
|
// import SalesReports from '@/http/controllers/SalesReports';
|
||||||
@@ -29,24 +29,24 @@ export default (app) => {
|
|||||||
// app.use('/api/oauth2', OAuth2.router());
|
// app.use('/api/oauth2', OAuth2.router());
|
||||||
app.use('/api/auth', Authentication.router());
|
app.use('/api/auth', Authentication.router());
|
||||||
app.use('/api/currencies', Currencies.router());
|
app.use('/api/currencies', Currencies.router());
|
||||||
app.use('/api/users', Users.router());
|
// app.use('/api/users', Users.router());
|
||||||
app.use('/api/roles', Roles.router());
|
// app.use('/api/roles', Roles.router());
|
||||||
app.use('/api/accounts', Accounts.router());
|
app.use('/api/accounts', Accounts.router());
|
||||||
app.use('/api/account_types', AccountTypes.router());
|
app.use('/api/account_types', AccountTypes.router());
|
||||||
app.use('/api/accounting', Accounting.router());
|
app.use('/api/accounting', Accounting.router());
|
||||||
app.use('/api/accounts_opening_balances', AccountOpeningBalance.router());
|
// app.use('/api/accounts_opening_balances', AccountOpeningBalance.router());
|
||||||
app.use('/api/views', Views.router());
|
app.use('/api/views', Views.router());
|
||||||
app.use('/api/fields', CustomFields.router());
|
// app.use('/api/fields', CustomFields.router());
|
||||||
app.use('/api/items', Items.router());
|
app.use('/api/items', Items.router());
|
||||||
app.use('/api/item_categories', ItemCategories.router());
|
app.use('/api/item_categories', ItemCategories.router());
|
||||||
app.use('/api/expenses', Expenses.router());
|
// app.use('/api/expenses', Expenses.router());
|
||||||
app.use('/api/financial_statements', FinancialStatements.router());
|
app.use('/api/financial_statements', FinancialStatements.router());
|
||||||
app.use('/api/options', Options.router());
|
app.use('/api/options', Options.router());
|
||||||
app.use('/api/budget_reports', BudgetReports.router());
|
// app.use('/api/budget_reports', BudgetReports.router());
|
||||||
// app.use('/api/customers', Customers.router());
|
// app.use('/api/customers', Customers.router());
|
||||||
// app.use('/api/suppliers', Suppliers.router());
|
// app.use('/api/suppliers', Suppliers.router());
|
||||||
// app.use('/api/bills', Bills.router());
|
// app.use('/api/bills', Bills.router());
|
||||||
app.use('/api/budget', Budget.router());
|
// app.use('/api/budget', Budget.router());
|
||||||
app.use('/api/resources', Resources.router());
|
app.use('/api/resources', Resources.router());
|
||||||
app.use('/api/exchange_rates', ExchangeRates.router());
|
app.use('/api/exchange_rates', ExchangeRates.router());
|
||||||
// app.use('/api/currency_adjustment', CurrencyAdjustment.router());
|
// app.use('/api/currency_adjustment', CurrencyAdjustment.router());
|
||||||
|
|||||||
52
server/src/http/middleware/TenancyMiddleware.js
Normal file
52
server/src/http/middleware/TenancyMiddleware.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import TenantsManager from '@/system/TenantsManager';
|
||||||
|
import Model from '@/models/Model';
|
||||||
|
|
||||||
|
function loadModelsFromDirectory() {
|
||||||
|
const models = {};
|
||||||
|
fs.readdirSync('src/models/').forEach((filename) => {
|
||||||
|
const model = {
|
||||||
|
path: path.join(__dirname, 'src/models/', filename),
|
||||||
|
name: filename.replace(/\.[^/.]+$/, ''),
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line global-require
|
||||||
|
model.resource = require(`@/models/${model.name}`);
|
||||||
|
models[model.name] = model;
|
||||||
|
});
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (req, res, next) => {
|
||||||
|
const { organization: organizationId } = req.query;
|
||||||
|
const notFoundOrganization = () => res.status(400).send({
|
||||||
|
errors: [{ type: 'ORGANIZATION.ID.NOT.FOUND' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!organizationId) {
|
||||||
|
return notFoundOrganization();
|
||||||
|
}
|
||||||
|
const tenant = await TenantsManager.getTenant(organizationId);
|
||||||
|
|
||||||
|
if (!tenant) {
|
||||||
|
return notFoundOrganization();
|
||||||
|
}
|
||||||
|
const knex = TenantsManager.knexInstance(organizationId);
|
||||||
|
const models = loadModelsFromDirectory();
|
||||||
|
|
||||||
|
Model.knexBinded = knex;
|
||||||
|
|
||||||
|
req.knex = knex;
|
||||||
|
req.organizationId = organizationId;
|
||||||
|
req.models = {
|
||||||
|
...Object.values(models).reduce((acc, model) => {
|
||||||
|
if (model.resource
|
||||||
|
&& model.resource.default
|
||||||
|
&& Object.getPrototypeOf(model.resource.default) === Model) {
|
||||||
|
acc[model.name] = model.resource.default.bindKnex(knex);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {}),
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import User from '@/models/User';
|
import SystemUser from '@/system/models/SystemUser';
|
||||||
// import Auth from '@/models/Auth';
|
// import Auth from '@/models/Auth';
|
||||||
|
|
||||||
const authMiddleware = (req, res, next) => {
|
const authMiddleware = (req, res, next) => {
|
||||||
@@ -25,7 +25,7 @@ const authMiddleware = (req, res, next) => {
|
|||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
req.user = await User.query().findById(decoded._id);
|
req.user = await SystemUser.query().findById(decoded._id);
|
||||||
// Auth.setAuthenticatedUser(req.user);
|
// Auth.setAuthenticatedUser(req.user);
|
||||||
|
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import { flatten } from 'lodash';
|
import { flatten } from 'lodash';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
import {
|
import {
|
||||||
buildFilterQuery,
|
buildFilterQuery,
|
||||||
buildSortColumnQuery,
|
buildSortColumnQuery,
|
||||||
} from '@/lib/ViewRolesBuilder';
|
} from '@/lib/ViewRolesBuilder';
|
||||||
export default class Account extends BaseModel {
|
|
||||||
|
export default class Account extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -36,7 +37,7 @@ export default class Account extends BaseModel {
|
|||||||
},
|
},
|
||||||
sortColumnBuilder(query, columnKey, direction) {
|
sortColumnBuilder(query, columnKey, direction) {
|
||||||
buildSortColumnQuery(Account.tableName, columnKey, direction)(query);
|
buildSortColumnQuery(Account.tableName, columnKey, direction)(query);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ export default class Account extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
type: {
|
type: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: AccountType.default,
|
modelClass: this.relationBindKnex(AccountType.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'accounts.accountTypeId',
|
from: 'accounts.accountTypeId',
|
||||||
to: 'account_types.id',
|
to: 'account_types.id',
|
||||||
@@ -66,7 +67,7 @@ export default class Account extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
balance: {
|
balance: {
|
||||||
relation: Model.HasOneRelation,
|
relation: Model.HasOneRelation,
|
||||||
modelClass: AccountBalance.default,
|
modelClass: this.relationBindKnex(AccountBalance.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'accounts.id',
|
from: 'accounts.id',
|
||||||
to: 'account_balances.accountId',
|
to: 'account_balances.accountId',
|
||||||
@@ -78,7 +79,7 @@ export default class Account extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
transactions: {
|
transactions: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: AccountTransaction.default,
|
modelClass: this.relationBindKnex(AccountTransaction.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'accounts.id',
|
from: 'accounts.id',
|
||||||
to: 'accounts_transactions.accountId',
|
to: 'accounts_transactions.accountId',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class AccountBalance extends BaseModel {
|
export default class AccountBalance extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -18,9 +18,9 @@ export default class AccountBalance extends BaseModel {
|
|||||||
return {
|
return {
|
||||||
account: {
|
account: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'account_balance.account_id',
|
from: 'account_balances.account_id',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class AccountTransaction extends BaseModel {
|
export default class AccountTransaction extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +70,7 @@ export default class AccountTransaction extends BaseModel {
|
|||||||
return {
|
return {
|
||||||
account: {
|
account: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'accounts_transactions.accountId',
|
from: 'accounts_transactions.accountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// import path from 'path';
|
// import path from 'path';
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class AccountType extends BaseModel {
|
export default class AccountType extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -22,7 +22,7 @@ export default class AccountType extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
accounts: {
|
accounts: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'account_types.id',
|
from: 'account_types.id',
|
||||||
to: 'accounts.accountTypeId',
|
to: 'accounts.accountTypeId',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/Model';
|
||||||
|
|
||||||
export default class Budget extends BaseModel {
|
export default class Budget extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class Budget extends BaseModel {
|
export default class Budget extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class Currency extends BaseModel {
|
export default class Currency extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,10 +1,54 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import bcrypt from 'bcryptjs';
|
||||||
|
import { Model } from 'objection';
|
||||||
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
// import PermissionsService from '@/services/PermissionsService';
|
||||||
|
|
||||||
|
export default class TenantUser extends TenantModel {
|
||||||
|
// ...PermissionsService
|
||||||
|
|
||||||
|
static get virtualAttributes() {
|
||||||
|
return ['fullName'];
|
||||||
|
}
|
||||||
|
|
||||||
export default class ExchangeRate extends BaseModel {
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
static get tableName() {
|
static get tableName() {
|
||||||
return 'exchange_rates';
|
return 'users';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const Role = require('@/models/Role');
|
||||||
|
|
||||||
|
return {
|
||||||
|
roles: {
|
||||||
|
relation: Model.ManyToManyRelation,
|
||||||
|
modelClass: this.relationBindKnex(Role.default),
|
||||||
|
join: {
|
||||||
|
from: 'users.id',
|
||||||
|
through: {
|
||||||
|
from: 'user_has_roles.userId',
|
||||||
|
to: 'user_has_roles.roleId',
|
||||||
|
},
|
||||||
|
to: 'roles.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the password of the user.
|
||||||
|
* @param {String} password - The given password.
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
verifyPassword(password) {
|
||||||
|
return bcrypt.compareSync(password, this.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullName() {
|
||||||
|
return `${this.firstName} ${this.lastName}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
import {viewRolesBuilder} from '@/lib/ViewRolesBuilder';
|
import { viewRolesBuilder } from '@/lib/ViewRolesBuilder';
|
||||||
export default class Expense extends BaseModel {
|
|
||||||
|
export default class Expense extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -60,12 +61,12 @@ export default class Expense extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Account = require('@/models/Account');
|
const Account = require('@/models/Account');
|
||||||
const User = require('@/models/User');
|
const User = require('@/models/TenantUser');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentAccount: {
|
paymentAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'expenses.paymentAccountId',
|
from: 'expenses.paymentAccountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
@@ -74,7 +75,7 @@ export default class Expense extends BaseModel {
|
|||||||
|
|
||||||
expenseAccount: {
|
expenseAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'expenses.expenseAccountId',
|
from: 'expenses.expenseAccountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
@@ -83,7 +84,7 @@ export default class Expense extends BaseModel {
|
|||||||
|
|
||||||
user: {
|
user: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: User.default,
|
modelClass: this.relationBindKnex(User.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'expenses.userId',
|
from: 'expenses.userId',
|
||||||
to: 'users.id',
|
to: 'users.id',
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import path from 'path';
|
import TenantModel from '@/models/TenantModel';
|
||||||
import BaseModel from '@/models/Model';
|
|
||||||
import {
|
import {
|
||||||
buildFilterQuery,
|
buildFilterQuery,
|
||||||
} from '@/lib/ViewRolesBuilder';
|
} from '@/lib/ViewRolesBuilder';
|
||||||
|
|
||||||
export default class Item extends BaseModel {
|
export default class Item extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -34,24 +33,12 @@ export default class Item extends BaseModel {
|
|||||||
const ItemCategory = require('@/models/ItemCategory');
|
const ItemCategory = require('@/models/ItemCategory');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
|
||||||
* Item may has many meta data.
|
|
||||||
*/
|
|
||||||
metadata: {
|
|
||||||
relation: Model.HasManyRelation,
|
|
||||||
modelBase: path.join(__dirname, 'ItemMetadata'),
|
|
||||||
join: {
|
|
||||||
from: 'items.id',
|
|
||||||
to: 'items_metadata.item_id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item may belongs to cateogory model.
|
* Item may belongs to cateogory model.
|
||||||
*/
|
*/
|
||||||
category: {
|
category: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: ItemCategory.default,
|
modelClass: this.relationBindKnex(ItemCategory.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items.categoryId',
|
from: 'items.categoryId',
|
||||||
to: 'items_categories.id',
|
to: 'items_categories.id',
|
||||||
@@ -60,7 +47,7 @@ export default class Item extends BaseModel {
|
|||||||
|
|
||||||
costAccount: {
|
costAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items.costAccountId',
|
from: 'items.costAccountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
@@ -69,7 +56,7 @@ export default class Item extends BaseModel {
|
|||||||
|
|
||||||
sellAccount: {
|
sellAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items.sellAccountId',
|
from: 'items.sellAccountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
@@ -78,7 +65,7 @@ export default class Item extends BaseModel {
|
|||||||
|
|
||||||
inventoryAccount: {
|
inventoryAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account.default,
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items.inventoryAccountId',
|
from: 'items.inventoryAccountId',
|
||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class ItemCategory extends BaseModel {
|
export default class ItemCategory extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -22,7 +22,7 @@ export default class ItemCategory extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
items: {
|
items: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: Item.default,
|
modelClass: this.relationBindKnex(Item.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items_categories.id',
|
from: 'items_categories.id',
|
||||||
to: 'items.categoryId',
|
to: 'items.categoryId',
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import path from 'path';
|
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class ItemMetadata extends BaseModel {
|
export default class ItemMetadata extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -21,13 +20,15 @@ export default class ItemMetadata extends BaseModel {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
|
const Item = require('@/models/Item');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Item category may has many items.
|
* Item category may has many items.
|
||||||
*/
|
*/
|
||||||
items: {
|
items: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelBase: path.join(__dirname, 'Item'),
|
modelBase: this.relationBindKnex(Item.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'items_metadata.item_id',
|
from: 'items_metadata.item_id',
|
||||||
to: 'items.id',
|
to: 'items.id',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class JournalEntry extends BaseModel {
|
export default class JournalEntry extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/Model';
|
||||||
|
|
||||||
export default class ManualJournal extends BaseModel {
|
export default class ManualJournal extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default {
|
|||||||
setExtraColumns(columns) {
|
setExtraColumns(columns) {
|
||||||
this.extraColumns = columns;
|
this.extraColumns = columns;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata database query.
|
* Metadata database query.
|
||||||
* @param {Object} query -
|
* @param {Object} query -
|
||||||
@@ -117,7 +117,7 @@ export default {
|
|||||||
metadata.markAsDeleted = true;
|
metadata.markAsDeleted = true;
|
||||||
}
|
}
|
||||||
this.shouldReload = true;
|
this.shouldReload = true;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all meta data of the given group.
|
* Remove all meta data of the given group.
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import {transform, snakeCase} from 'lodash';
|
import { snakeCase } from 'lodash';
|
||||||
import {mapKeysDeep} from '@/utils';
|
import { mapKeysDeep } from '@/utils';
|
||||||
import PaginationQueryBuilder from '@/models/Pagination';
|
import PaginationQueryBuilder from '@/models/Pagination';
|
||||||
|
|
||||||
export default class ModelBase extends Model {
|
export default class ModelBase extends Model {
|
||||||
|
|
||||||
|
static get knexBinded() {
|
||||||
|
return this.knexBindInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static set knexBinded(knex) {
|
||||||
|
this.knexBindInstance = knex;
|
||||||
|
}
|
||||||
|
|
||||||
static get collection() {
|
static get collection() {
|
||||||
return Array;
|
return Array;
|
||||||
}
|
}
|
||||||
@@ -22,11 +31,14 @@ export default class ModelBase extends Model {
|
|||||||
return snakeCase(key);
|
return snakeCase(key);
|
||||||
});
|
});
|
||||||
const parsedJson = super.$formatJson(transformed, opt);
|
const parsedJson = super.$formatJson(transformed, opt);
|
||||||
|
|
||||||
return parsedJson;
|
return parsedJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get QueryBuilder() {
|
static get QueryBuilder() {
|
||||||
return PaginationQueryBuilder;
|
return PaginationQueryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static relationBindKnex(model) {
|
||||||
|
return this.knexBinded ? model.bindKnex(this.knexBinded) : model;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import bookshelf from './bookshelf';
|
|
||||||
|
|
||||||
const OAuthClient = bookshelf.Model.extend({
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Table name
|
|
||||||
*/
|
|
||||||
tableName: 'oauth_clients',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timestamp columns.
|
|
||||||
*/
|
|
||||||
hasTimestamps: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default bookshelf.model('OAuthClient', OAuthClient);
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import OAuthClient from '@/models/OAuthClient';
|
|
||||||
import OAuthToken from '@/models/OAuthToken';
|
|
||||||
import User from '@/models/User';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Retrieve the access token.
|
|
||||||
* @param {String} bearerToken -
|
|
||||||
*/
|
|
||||||
async getAccessToken(bearerToken) {
|
|
||||||
const token = await OAuthClient.where({
|
|
||||||
access_token: bearerToken,
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
return {
|
|
||||||
accessToken: token.attributes.access_token,
|
|
||||||
client: {
|
|
||||||
id: token.attributes.client_id,
|
|
||||||
},
|
|
||||||
expires: token.attributes.access_token_expires_on,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the client from client id and secret.
|
|
||||||
* @param {Number} clientId -
|
|
||||||
* @param {String} clientSecret -
|
|
||||||
*/
|
|
||||||
async getClient(clientId, clientSecret) {
|
|
||||||
const token = await OAuthClient.where({
|
|
||||||
client_id: clientId,
|
|
||||||
client_secret: clientSecret,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!token) { return {}; }
|
|
||||||
|
|
||||||
return {
|
|
||||||
clientId: token.attributes.client_id,
|
|
||||||
clientSecret: token.attributes.client_secret,
|
|
||||||
grants: ['password'],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get specific user with given username and password.
|
|
||||||
*/
|
|
||||||
async getUser(username, password) {
|
|
||||||
const user = await User.query((query) => {
|
|
||||||
query.where('username', username);
|
|
||||||
query.where('password', password);
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...user.attributes,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the access token.
|
|
||||||
* @param {Object} token -
|
|
||||||
* @param {Object} client -
|
|
||||||
* @param {Object} user -
|
|
||||||
*/
|
|
||||||
async saveAccessToken(token, client, user) {
|
|
||||||
const oauthToken = OAuthToken.forge({
|
|
||||||
access_token: token.accessToken,
|
|
||||||
access_token_expires_on: token.accessTokenExpiresOn,
|
|
||||||
client_id: client.id,
|
|
||||||
refresh_token: token.refreshToken,
|
|
||||||
refresh_token_expires_on: token.refreshTokenExpiresOn,
|
|
||||||
user_id: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await oauthToken.save();
|
|
||||||
|
|
||||||
return {
|
|
||||||
client: { id: client.id },
|
|
||||||
user: { id: user.id },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import bookshelf from './bookshelf';
|
|
||||||
|
|
||||||
const OAuthToken = bookshelf.Model.extend({
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Table name
|
|
||||||
*/
|
|
||||||
tableName: 'oauth_tokens',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timestamp columns.
|
|
||||||
*/
|
|
||||||
hasTimestamps: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default bookshelf.model('OAuthToken', OAuthToken);
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Model from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class PasswordResets extends Model {
|
export default class PasswordResets extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class Permission extends BaseModel {
|
export default class Permission extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name of Role model.
|
* Table name of Role model.
|
||||||
* @type {String}
|
* @type {String}
|
||||||
@@ -15,18 +15,20 @@ export default class Permission extends BaseModel {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
|
const Role = require('@/models/Role');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Permission model may belongs to role model.
|
* Permission model may belongs to role model.
|
||||||
*/
|
*/
|
||||||
role: {
|
// role: {
|
||||||
relation: Model.BelongsToOneRelation,
|
// relation: Model.BelongsToOneRelation,
|
||||||
modelBase: path.join(__dirname, 'Role'),
|
// modelBase: path.join(__dirname, 'Role').bindKnex(this.knexBinded),
|
||||||
join: {
|
// join: {
|
||||||
from: 'permissions.role_id',
|
// from: 'permissions.role_id',
|
||||||
to: 'roles.id',
|
// to: 'roles.id',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
// resource: {
|
// resource: {
|
||||||
// relation: Model.BelongsToOneRelation,
|
// relation: Model.BelongsToOneRelation,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import path from 'path';
|
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class Resource extends BaseModel {
|
export default class Resource extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +30,7 @@ export default class Resource extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
views: {
|
views: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: View.default,
|
modelClass: this.relationBindKnex(View.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'resources.id',
|
from: 'resources.id',
|
||||||
to: 'views.resourceId',
|
to: 'views.resourceId',
|
||||||
@@ -43,7 +42,7 @@ export default class Resource extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
fields: {
|
fields: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: ResourceField.default,
|
modelClass: this.relationBindKnex(ResourceField.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'resources.id',
|
from: 'resources.id',
|
||||||
to: 'resource_fields.resourceId',
|
to: 'resource_fields.resourceId',
|
||||||
@@ -55,7 +54,7 @@ export default class Resource extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
permissions: {
|
permissions: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: Permission.default,
|
modelClass: this.relationBindKnex(Permission.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'resources.id',
|
from: 'resources.id',
|
||||||
through: {
|
through: {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { snakeCase } from 'lodash';
|
import { snakeCase } from 'lodash';
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class ResourceField extends BaseModel {
|
export default class ResourceField extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -51,15 +51,17 @@ export default class ResourceField extends BaseModel {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
|
const Resource = require('@/models/Resource');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Resource field may belongs to resource model.
|
* Resource field may belongs to resource model.
|
||||||
*/
|
*/
|
||||||
resource: {
|
resource: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelBase: path.join(__dirname, 'Resource'),
|
modelClass: this.relationBindKnex(Resource.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'resource_fields.resource_id',
|
from: 'resource_fields.resourceId',
|
||||||
to: 'resources.id',
|
to: 'resources.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
import ResourceFieldMetadataCollection from '@/collection/ResourceFieldMetadataCollection';
|
import ResourceFieldMetadataCollection from '@/collection/ResourceFieldMetadataCollection';
|
||||||
|
|
||||||
export default class ResourceFieldMetadata extends BaseModel {
|
export default class ResourceFieldMetadata extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class Role extends BaseModel {
|
export default class Role extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name of Role model.
|
* Table name of Role model.
|
||||||
* @type {String}
|
* @type {String}
|
||||||
@@ -23,7 +23,7 @@ export default class Role extends BaseModel {
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Permission = require('@/models/Permission');
|
const Permission = require('@/models/Permission');
|
||||||
const Resource = require('@/models/Resource');
|
const Resource = require('@/models/Resource');
|
||||||
const User = require('@/models/User');
|
const User = require('@/models/TenantUser');
|
||||||
const ResourceField = require('@/models/ResourceField');
|
const ResourceField = require('@/models/ResourceField');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -32,7 +32,7 @@ export default class Role extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
permissions: {
|
permissions: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: Permission.default,
|
modelClass: Permission.default.bindKnex(this.knexBinded),
|
||||||
join: {
|
join: {
|
||||||
from: 'roles.id',
|
from: 'roles.id',
|
||||||
through: {
|
through: {
|
||||||
@@ -48,7 +48,7 @@ export default class Role extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
resources: {
|
resources: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: Resource.default,
|
modelClass: Resource.default.bindKnex(this.knexBinded),
|
||||||
join: {
|
join: {
|
||||||
from: 'roles.id',
|
from: 'roles.id',
|
||||||
through: {
|
through: {
|
||||||
@@ -64,11 +64,11 @@ export default class Role extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
field: {
|
field: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: ResourceField.default,
|
modelClass: ResourceField.default.bindKnex(this.knexBinded),
|
||||||
join: {
|
join: {
|
||||||
from: 'roles.fieldId',
|
from: 'roles.fieldId',
|
||||||
to: 'resource_fields.id',
|
to: 'resource_fields.id',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +76,7 @@ export default class Role extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
users: {
|
users: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: User.default,
|
modelClass: User.default.bindKnex(this.knexBinded),
|
||||||
join: {
|
join: {
|
||||||
from: 'roles.id',
|
from: 'roles.id',
|
||||||
through: {
|
through: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
import Auth from './Auth';
|
import Auth from './Auth';
|
||||||
|
|
||||||
export default class Setting extends BaseModel {
|
export default class Setting extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
4
server/src/models/TenantModel.js
Normal file
4
server/src/models/TenantModel.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import BaseModel from '@/models/Model';
|
||||||
|
|
||||||
|
export default class TenantModel extends BaseModel{
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { Model } from 'objection';
|
|||||||
import BaseModel from '@/models/Model';
|
import BaseModel from '@/models/Model';
|
||||||
// import PermissionsService from '@/services/PermissionsService';
|
// import PermissionsService from '@/services/PermissionsService';
|
||||||
|
|
||||||
export default class User extends BaseModel {
|
export default class TenantUser extends BaseModel {
|
||||||
// ...PermissionsService
|
// ...PermissionsService
|
||||||
|
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
@@ -26,7 +26,7 @@ export default class User extends BaseModel {
|
|||||||
return {
|
return {
|
||||||
roles: {
|
roles: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: Role.default,
|
modelClass: this.relationBindKnex(Role.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'users.id',
|
from: 'users.id',
|
||||||
through: {
|
through: {
|
||||||
@@ -51,4 +51,4 @@ export default class User extends BaseModel {
|
|||||||
fullName() {
|
fullName() {
|
||||||
return `${this.firstName} ${this.lastName}`;
|
return `${this.firstName} ${this.lastName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import path from 'path';
|
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class View extends BaseModel {
|
export default class View extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -24,7 +23,7 @@ export default class View extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
resource: {
|
resource: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Resource.default,
|
modelClass: this.relationBindKnex(Resource.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'views.resourceId',
|
from: 'views.resourceId',
|
||||||
to: 'resources.id',
|
to: 'resources.id',
|
||||||
@@ -36,7 +35,7 @@ export default class View extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
columns: {
|
columns: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: ViewColumn.default,
|
modelClass: this.relationBindKnex(ViewColumn.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'views.id',
|
from: 'views.id',
|
||||||
to: 'view_has_columns.viewId',
|
to: 'view_has_columns.viewId',
|
||||||
@@ -48,7 +47,7 @@ export default class View extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
roles: {
|
roles: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: ViewRole.default,
|
modelClass: this.relationBindKnex(ViewRole.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'views.id',
|
from: 'views.id',
|
||||||
to: 'view_roles.viewId',
|
to: 'view_roles.viewId',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class ViewColumn extends BaseModel {
|
export default class ViewColumn extends TenantModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -29,9 +29,9 @@ export default class ViewColumn extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
field: {
|
field: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: ResourceField.default,
|
modelClass: this.relationBindKnex(ResourceField.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'view_columns.fieldId',
|
from: 'view_has_columns.fieldId',
|
||||||
to: 'resource_fields.id',
|
to: 'resource_fields.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import BaseModel from '@/models/Model';
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
export default class ViewRole extends BaseModel {
|
export default class ViewRole extends TenantModel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
@@ -43,7 +43,7 @@ export default class ViewRole extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
view: {
|
view: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: View.default,
|
modelClass: this.relationBindKnex(View.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'view_roles.viewId',
|
from: 'view_roles.viewId',
|
||||||
to: 'views.id',
|
to: 'views.id',
|
||||||
@@ -55,7 +55,7 @@ export default class ViewRole extends BaseModel {
|
|||||||
*/
|
*/
|
||||||
field: {
|
field: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: ResourceField.default,
|
modelClass: this.relationBindKnex(ResourceField.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'view_roles.fieldId',
|
from: 'view_roles.fieldId',
|
||||||
to: 'resource_fields.id',
|
to: 'resource_fields.id',
|
||||||
|
|||||||
13
server/src/services/Logger/index.js
Normal file
13
server/src/services/Logger/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import winston from 'winston';
|
||||||
|
|
||||||
|
const transports = {
|
||||||
|
console: new winston.transports.Console({ level: 'warn' }),
|
||||||
|
file: new winston.transports.File({ filename: 'stdout.log' }),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default winston.createLogger({
|
||||||
|
transports: [
|
||||||
|
transports.console,
|
||||||
|
transports.file,
|
||||||
|
],
|
||||||
|
});
|
||||||
58
server/src/system/TenantsManager.js
Normal file
58
server/src/system/TenantsManager.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import Knex from 'knex';
|
||||||
|
import Tenant from '@/system/models/Tenant';
|
||||||
|
import config from '@/../config/config';
|
||||||
|
|
||||||
|
export default class TenantsManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.knexCache = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getTenant(organizationId) {
|
||||||
|
const tenant = await Tenant.query()
|
||||||
|
.where('organization_id', organizationId).first();
|
||||||
|
|
||||||
|
return tenant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all tenants metadata from system storage.
|
||||||
|
*/
|
||||||
|
static getAllTenants() {
|
||||||
|
return Tenant.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the given organization id knex configuration.
|
||||||
|
* @param {String} organizationId -
|
||||||
|
*/
|
||||||
|
static getTenantKnexConfig(organizationId) {
|
||||||
|
return {
|
||||||
|
client: config.tenant.db_client,
|
||||||
|
connection: {
|
||||||
|
host: config.tenant.db_host,
|
||||||
|
user: config.tenant.db_user,
|
||||||
|
password: config.tenant.db_password,
|
||||||
|
database: `${config.tenant.db_name_prefix}${organizationId}`,
|
||||||
|
charset: config.tenant.charset,
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: config.tenant.migrations_dir,
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: config.tenant.seeds_dir,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static knexInstance(organizationId) {
|
||||||
|
const knexCache = new Map();
|
||||||
|
let knex = knexCache.get(organizationId);
|
||||||
|
|
||||||
|
if (!knex) {
|
||||||
|
knex = Knex(this.getTenantKnexConfig(organizationId));
|
||||||
|
knexCache.set(organizationId, knex);
|
||||||
|
}
|
||||||
|
return knex;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema.createTable('users', (table) => {
|
||||||
|
table.increments();
|
||||||
|
table.string('first_name');
|
||||||
|
table.string('last_name');
|
||||||
|
table.string('email').unique();
|
||||||
|
table.string('phone_number').unique();
|
||||||
|
table.string('password');
|
||||||
|
table.boolean('active');
|
||||||
|
table.integer('role_id').unique();
|
||||||
|
table.string('language');
|
||||||
|
table.date('last_login_at');
|
||||||
|
table.integer('tenant_id').unsigned();
|
||||||
|
table.timestamps();
|
||||||
|
}).then(() => {
|
||||||
|
// knex.seed.run({
|
||||||
|
// specific: 'seed_users.js',
|
||||||
|
// })
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (knex) {
|
||||||
|
return knex.schema.dropTableIfExists('users');
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.createTable('tenants', (table) => {
|
||||||
|
table.bigIncrements();
|
||||||
|
table.string('organization_id');
|
||||||
|
table.timestamps();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.dropTableIfExists('tenants');
|
||||||
|
};
|
||||||
4
server/src/system/models/SystemModel.js
Normal file
4
server/src/system/models/SystemModel.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import BaseModel from '@/models/Model';
|
||||||
|
|
||||||
|
export default class SystemModel extends BaseModel{
|
||||||
|
}
|
||||||
29
server/src/system/models/SystemOption.js
Normal file
29
server/src/system/models/SystemOption.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { mixin } from 'objection';
|
||||||
|
import SystemModel from '@/system/models/SystemModel';
|
||||||
|
import MetableCollection from '@/lib/Metable/MetableCollection';
|
||||||
|
|
||||||
|
export default class Option extends mixin(SystemModel, [mixin]) {
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'options';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the model query.
|
||||||
|
* @param {...any} args -
|
||||||
|
*/
|
||||||
|
static query(...args) {
|
||||||
|
return super.query(...args).runAfter((result) => {
|
||||||
|
if (result instanceof MetableCollection) {
|
||||||
|
result.setModel(Option);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get collection() {
|
||||||
|
return MetableCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
server/src/system/models/SystemUser.js
Normal file
39
server/src/system/models/SystemUser.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
import SystemModel from '@/system/models/SystemModel';
|
||||||
|
|
||||||
|
export default class SystemUser extends SystemModel {
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'users';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const Tenant = require('@/system/models/Tenant');
|
||||||
|
|
||||||
|
return {
|
||||||
|
tenant: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Tenant.default,
|
||||||
|
join: {
|
||||||
|
from: 'users.tenant_id',
|
||||||
|
to: 'tenants.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the password of the user.
|
||||||
|
* @param {String} password - The given password.
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
verifyPassword(password) {
|
||||||
|
return bcrypt.compareSync(password, this.password);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
server/src/system/models/Tenant.js
Normal file
10
server/src/system/models/Tenant.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import BaseModel from '@/models/Model';
|
||||||
|
|
||||||
|
export default class Tenant extends BaseModel {
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'tenants';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user