mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
WIP pass the failed tests.
This commit is contained in:
@@ -1,282 +1,284 @@
|
||||
import knexFactory from 'knex-factory';
|
||||
import KnexFactory from '@/lib/KnexFactory';
|
||||
import faker from 'faker';
|
||||
import knex from '@/database/knex';
|
||||
import { hashPassword } from '@/utils';
|
||||
|
||||
const factory = knexFactory(knex);
|
||||
|
||||
factory.define('user', 'users', async () => {
|
||||
const hashedPassword = await hashPassword('admin');
|
||||
return {
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
email: faker.internet.email(),
|
||||
phone_number: faker.phone.phoneNumberFormat().replace('-', ''),
|
||||
active: 1,
|
||||
password: hashedPassword,
|
||||
};
|
||||
});
|
||||
export default (tenantDb) => {
|
||||
const factory = new KnexFactory(tenantDb);
|
||||
|
||||
factory.define('password_reset', 'password_resets', async () => {
|
||||
const user = await faker.create('user');
|
||||
return {
|
||||
user_id: user.id,
|
||||
token: faker.lorem.slug,
|
||||
};
|
||||
});
|
||||
factory.define('user', 'users', async () => {
|
||||
// const hashedPassword = await hashPassword('admin');
|
||||
|
||||
factory.define('account_type', 'account_types', async () => ({
|
||||
name: faker.lorem.words(2),
|
||||
normal: 'debit',
|
||||
}));
|
||||
return {
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
email: faker.internet.email(),
|
||||
phone_number: faker.phone.phoneNumberFormat().replace('-', ''),
|
||||
active: 1,
|
||||
// password: hashedPassword,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('account_balance', 'account_balances', async () => {
|
||||
const account = await factory.create('account');
|
||||
factory.define('password_reset', 'password_resets', async () => {
|
||||
return {
|
||||
user_id: null,
|
||||
token: faker.lorem.slug,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
amount: faker.random.number(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
factory.define('account_type', 'account_types', async () => ({
|
||||
name: faker.lorem.words(2),
|
||||
normal: 'debit',
|
||||
}));
|
||||
|
||||
factory.define('account', 'accounts', async () => {
|
||||
const accountType = await factory.create('account_type');
|
||||
return {
|
||||
factory.define('account_balance', 'account_balances', async () => {
|
||||
const account = await factory.create('account');
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
amount: faker.random.number(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('account', 'accounts', async () => {
|
||||
const accountType = await factory.create('account_type');
|
||||
return {
|
||||
name: faker.lorem.word(),
|
||||
code: faker.random.number(),
|
||||
account_type_id: accountType.id,
|
||||
description: faker.lorem.paragraph(),
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('account_transaction', 'accounts_transactions', async () => {
|
||||
const account = await factory.create('account');
|
||||
const user = await factory.create('user');
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
credit: faker.random.number(),
|
||||
debit: 0,
|
||||
user_id: user.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('manual_journal', 'manual_journals', async () => {
|
||||
const user = await factory.create('user');
|
||||
|
||||
return {
|
||||
journal_number: faker.random.number(),
|
||||
transaction_type: '',
|
||||
amount: faker.random.number(),
|
||||
date: faker.date.future,
|
||||
status: 1,
|
||||
user_id: user.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('item_category', 'items_categories', () => ({
|
||||
name: faker.name.firstName(),
|
||||
description: faker.lorem.text(),
|
||||
parent_category_id: null,
|
||||
}));
|
||||
|
||||
factory.define('item_metadata', 'items_metadata', async () => {
|
||||
const item = await factory.create('item');
|
||||
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
value: faker.lorem.word(),
|
||||
item_id: item.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('item', 'items', async () => {
|
||||
const category = await factory.create('item_category');
|
||||
const costAccount = await factory.create('account');
|
||||
const sellAccount = await factory.create('account');
|
||||
const inventoryAccount = await factory.create('account');
|
||||
return {
|
||||
name: faker.lorem.word(),
|
||||
note: faker.lorem.paragraph(),
|
||||
cost_price: faker.random.number(),
|
||||
sell_price: faker.random.number(),
|
||||
cost_account_id: costAccount.id,
|
||||
sell_account_id: sellAccount.id,
|
||||
inventory_account_id: inventoryAccount.id,
|
||||
category_id: category.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('setting', 'settings', async () => {
|
||||
const user = await factory.create('user');
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
user_id: user.id,
|
||||
type: 'string',
|
||||
value: faker.lorem.words(),
|
||||
group: 'default',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('role', 'roles', async () => ({
|
||||
name: faker.lorem.word(),
|
||||
code: faker.random.number(),
|
||||
account_type_id: accountType.id,
|
||||
description: faker.lorem.paragraph(),
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('account_transaction', 'accounts_transactions', async () => {
|
||||
const account = await factory.create('account');
|
||||
const user = await factory.create('user');
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
credit: faker.random.number(),
|
||||
debit: 0,
|
||||
user_id: user.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('manual_journal', 'manual_journals', async () => {
|
||||
const user = await factory.create('user');
|
||||
|
||||
return {
|
||||
journal_number: faker.random.number(),
|
||||
transaction_type: '',
|
||||
amount: faker.random.number(),
|
||||
date: faker.date.future,
|
||||
status: 1,
|
||||
user_id: user.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('item_category', 'items_categories', () => ({
|
||||
name: faker.name.firstName(),
|
||||
description: faker.lorem.text(),
|
||||
parent_category_id: null,
|
||||
}));
|
||||
|
||||
factory.define('item_metadata', 'items_metadata', async () => {
|
||||
const item = await factory.create('item');
|
||||
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
value: faker.lorem.word(),
|
||||
item_id: item.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('item', 'items', async () => {
|
||||
const category = await factory.create('item_category');
|
||||
const costAccount = await factory.create('account');
|
||||
const sellAccount = await factory.create('account');
|
||||
const inventoryAccount = await factory.create('account');
|
||||
return {
|
||||
name: faker.lorem.word(),
|
||||
note: faker.lorem.paragraph(),
|
||||
cost_price: faker.random.number(),
|
||||
sell_price: faker.random.number(),
|
||||
cost_account_id: costAccount.id,
|
||||
sell_account_id: sellAccount.id,
|
||||
inventory_account_id: inventoryAccount.id,
|
||||
category_id: category.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('setting', 'settings', async () => {
|
||||
const user = await factory.create('user');
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
user_id: user.id,
|
||||
type: 'string',
|
||||
value: faker.lorem.words(),
|
||||
group: 'default',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('role', 'roles', async () => ({
|
||||
name: faker.lorem.word(),
|
||||
description: faker.lorem.words(),
|
||||
predefined: false,
|
||||
}));
|
||||
|
||||
factory.define('user_has_role', 'user_has_roles', async () => {
|
||||
const user = await factory.create('user');
|
||||
const role = await factory.create('role');
|
||||
|
||||
return {
|
||||
user_id: user.id,
|
||||
role_id: role.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('permission', 'permissions', async () => {
|
||||
const permissions = ['create', 'edit', 'delete', 'view', 'owner'];
|
||||
const randomPermission = permissions[Math.floor(Math.random() * permissions.length)];
|
||||
|
||||
return {
|
||||
name: randomPermission,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('role_has_permission', 'role_has_permissions', async () => {
|
||||
const permission = await factory.create('permission');
|
||||
const role = await factory.create('role');
|
||||
const resource = await factory.create('resource');
|
||||
|
||||
return {
|
||||
role_id: role.id,
|
||||
permission_id: permission.id,
|
||||
resource_id: resource.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('resource', 'resources', () => ({
|
||||
name: faker.lorem.word(),
|
||||
}));
|
||||
|
||||
factory.define('view', 'views', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
return {
|
||||
name: faker.lorem.word(),
|
||||
resource_id: resource.id,
|
||||
description: faker.lorem.words(),
|
||||
predefined: false,
|
||||
};
|
||||
});
|
||||
}));
|
||||
|
||||
factory.define('resource_field', 'resource_fields', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
const dataTypes = ['select', 'date', 'text'];
|
||||
factory.define('user_has_role', 'user_has_roles', async () => {
|
||||
const user = await factory.create('user');
|
||||
const role = await factory.create('role');
|
||||
|
||||
return {
|
||||
label_name: faker.lorem.words(),
|
||||
key: faker.lorem.slug(),
|
||||
data_type: dataTypes[Math.floor(Math.random() * dataTypes.length)],
|
||||
help_text: faker.lorem.words(),
|
||||
default: faker.lorem.word(),
|
||||
resource_id: resource.id,
|
||||
active: true,
|
||||
columnable: true,
|
||||
predefined: false,
|
||||
};
|
||||
});
|
||||
return {
|
||||
user_id: user.id,
|
||||
role_id: role.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('resource_custom_field_metadata', 'resource_custom_fields_metadata', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
factory.define('permission', 'permissions', async () => {
|
||||
const permissions = ['create', 'edit', 'delete', 'view', 'owner'];
|
||||
const randomPermission = permissions[Math.floor(Math.random() * permissions.length)];
|
||||
|
||||
return {
|
||||
resource_id: resource.id,
|
||||
resource_item_id: 1,
|
||||
key: faker.lorem.words(),
|
||||
value: faker.lorem.words(),
|
||||
};
|
||||
});
|
||||
return {
|
||||
name: randomPermission,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('view_role', 'view_roles', async () => {
|
||||
const view = await factory.create('view');
|
||||
const field = await factory.create('resource_field');
|
||||
factory.define('role_has_permission', 'role_has_permissions', async () => {
|
||||
const permission = await factory.create('permission');
|
||||
const role = await factory.create('role');
|
||||
const resource = await factory.create('resource');
|
||||
|
||||
return {
|
||||
view_id: view.id,
|
||||
index: faker.random.number(),
|
||||
field_id: field.id,
|
||||
value: '',
|
||||
comparator: '',
|
||||
};
|
||||
});
|
||||
return {
|
||||
role_id: role.id,
|
||||
permission_id: permission.id,
|
||||
resource_id: resource.id,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('view_column', 'view_has_columns', async () => {
|
||||
const view = await factory.create('view');
|
||||
const field = await factory.create('resource_field');
|
||||
factory.define('resource', 'resources', () => ({
|
||||
name: faker.lorem.word(),
|
||||
}));
|
||||
|
||||
return {
|
||||
field_id: field.id,
|
||||
view_id: view.id,
|
||||
// index: 1,
|
||||
};
|
||||
});
|
||||
factory.define('view', 'views', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
return {
|
||||
name: faker.lorem.word(),
|
||||
resource_id: resource.id,
|
||||
predefined: false,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('expense', 'expenses', async () => {
|
||||
const paymentAccount = await factory.create('account');
|
||||
const expenseAccount = await factory.create('account');
|
||||
const user = await factory.create('user');
|
||||
factory.define('resource_field', 'resource_fields', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
const dataTypes = ['select', 'date', 'text'];
|
||||
|
||||
return {
|
||||
payment_account_id: paymentAccount.id,
|
||||
expense_account_id: expenseAccount.id,
|
||||
user_id: user.id,
|
||||
amount: faker.random.number(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
return {
|
||||
label_name: faker.lorem.words(),
|
||||
key: faker.lorem.slug(),
|
||||
data_type: dataTypes[Math.floor(Math.random() * dataTypes.length)],
|
||||
help_text: faker.lorem.words(),
|
||||
default: faker.lorem.word(),
|
||||
resource_id: resource.id,
|
||||
active: true,
|
||||
columnable: true,
|
||||
predefined: false,
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('option', 'options', async () => {
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
value: faker.lorem.slug(),
|
||||
group: faker.lorem.slug(),
|
||||
};
|
||||
});
|
||||
factory.define('resource_custom_field_metadata', 'resource_custom_fields_metadata', async () => {
|
||||
const resource = await factory.create('resource');
|
||||
|
||||
factory.define('currency', 'currencies', async () => {
|
||||
return {
|
||||
currency_name: faker.lorem.slug(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
return {
|
||||
resource_id: resource.id,
|
||||
resource_item_id: 1,
|
||||
key: faker.lorem.words(),
|
||||
value: faker.lorem.words(),
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('exchange_rate', 'exchange_rates', async () => {
|
||||
return {
|
||||
date: '2020-02-02',
|
||||
currency_code: 'USD',
|
||||
exchange_rate: faker.random.number(),
|
||||
};
|
||||
});
|
||||
factory.define('view_role', 'view_roles', async () => {
|
||||
const view = await factory.create('view');
|
||||
const field = await factory.create('resource_field');
|
||||
|
||||
factory.define('budget', 'budgets', async () => {
|
||||
return {
|
||||
name: faker.lorem.slug(),
|
||||
fiscal_year: '2020',
|
||||
period: 'month',
|
||||
account_types: 'profit_loss',
|
||||
};
|
||||
});
|
||||
return {
|
||||
view_id: view.id,
|
||||
index: faker.random.number(),
|
||||
field_id: field.id,
|
||||
value: '',
|
||||
comparator: '',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('budget_entry', 'budget_entries', async () => {
|
||||
const budget = await factory.create('budget');
|
||||
const account = await factory.create('account');
|
||||
factory.define('view_column', 'view_has_columns', async () => {
|
||||
const view = await factory.create('view');
|
||||
const field = await factory.create('resource_field');
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
budget_id: budget.id,
|
||||
amount: 1000,
|
||||
order: 1,
|
||||
};
|
||||
});
|
||||
return {
|
||||
field_id: field.id,
|
||||
view_id: view.id,
|
||||
// index: 1,
|
||||
};
|
||||
});
|
||||
|
||||
export default factory;
|
||||
factory.define('expense', 'expenses', async () => {
|
||||
const paymentAccount = await factory.create('account');
|
||||
const expenseAccount = await factory.create('account');
|
||||
const user = await factory.create('user');
|
||||
|
||||
return {
|
||||
payment_account_id: paymentAccount.id,
|
||||
expense_account_id: expenseAccount.id,
|
||||
user_id: user.id,
|
||||
amount: faker.random.number(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('option', 'options', async () => {
|
||||
return {
|
||||
key: faker.lorem.slug(),
|
||||
value: faker.lorem.slug(),
|
||||
group: faker.lorem.slug(),
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('currency', 'currencies', async () => {
|
||||
return {
|
||||
currency_name: faker.lorem.slug(),
|
||||
currency_code: 'USD',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('exchange_rate', 'exchange_rates', async () => {
|
||||
return {
|
||||
date: '2020-02-02',
|
||||
currency_code: 'USD',
|
||||
exchange_rate: faker.random.number(),
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('budget', 'budgets', async () => {
|
||||
return {
|
||||
name: faker.lorem.slug(),
|
||||
fiscal_year: '2020',
|
||||
period: 'month',
|
||||
account_types: 'profit_loss',
|
||||
};
|
||||
});
|
||||
|
||||
factory.define('budget_entry', 'budget_entries', async () => {
|
||||
const budget = await factory.create('budget');
|
||||
const account = await factory.create('account');
|
||||
|
||||
return {
|
||||
account_id: account.id,
|
||||
budget_id: budget.id,
|
||||
amount: 1000,
|
||||
order: 1,
|
||||
};
|
||||
});
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
16
server/src/database/factories/system.js
Normal file
16
server/src/database/factories/system.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import KnexFactory from '@/lib/KnexFactory';
|
||||
import systemDb from '@/database/knex';
|
||||
import faker from 'faker';
|
||||
|
||||
export default () => {
|
||||
const factory = new KnexFactory(systemDb);
|
||||
|
||||
factory.define('password_reset', 'password_resets', async () => {
|
||||
return {
|
||||
email: faker.lorem.email,
|
||||
token: faker.lorem.slug,
|
||||
};
|
||||
});
|
||||
|
||||
return factory;
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { knexSnakeCaseMappers } from 'objection';
|
||||
import knexfile from '@/../config/systemKnexfile';
|
||||
|
||||
const config = knexfile[process.env.NODE_ENV];
|
||||
|
||||
const knex = Knex({
|
||||
...config,
|
||||
...knexSnakeCaseMappers({ upperCase: true }),
|
||||
|
||||
@@ -4,10 +4,6 @@ import config from '@/../config/config';
|
||||
|
||||
const knexConfig = knexfile[process.env.NODE_ENV];
|
||||
|
||||
console.log({
|
||||
superUser: config.manager.superUser,
|
||||
superPassword: config.manager.superPassword,
|
||||
});
|
||||
const dbManager = knexManager.databaseManagerFactory({
|
||||
knex: knexConfig,
|
||||
dbManager: {
|
||||
|
||||
@@ -5,6 +5,8 @@ exports.up = (knex) => knex.schema.createTable('roles', (table) => {
|
||||
table.string('description');
|
||||
table.boolean('predefined').default(false);
|
||||
table.timestamps();
|
||||
});
|
||||
}).raw('ALTER TABLE `ROLES` AUTO_INCREMENT = 1000');
|
||||
|
||||
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTable('roles');
|
||||
|
||||
@@ -13,7 +13,7 @@ exports.up = function (knex) {
|
||||
|
||||
table.date('invite_accepted_at');
|
||||
table.timestamps();
|
||||
});
|
||||
}).raw('ALTER TABLE `USERS` AUTO_INCREMENT = 1000');;
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('oauth_tokens', (table) => {
|
||||
table.increments();
|
||||
table.string('access_token');
|
||||
table.date('access_token_expires_on');
|
||||
table.integer('client_id').unsigned();
|
||||
table.string('refresh_token');
|
||||
table.date('refresh_token_expires_on');
|
||||
table.integer('user_id').unsigned();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.dropTableIfExists('oauth_tokens');
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('oauth_clients', (table) => {
|
||||
table.increments();
|
||||
table.integer('client_id').unsigned();
|
||||
table.string('client_secret');
|
||||
table.string('redirect_uri');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('oauth_clients');
|
||||
@@ -7,7 +7,7 @@ exports.up = function (knex) {
|
||||
table.string('type');
|
||||
table.string('key');
|
||||
table.string('value');
|
||||
});
|
||||
}).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('settings');
|
||||
|
||||
@@ -4,6 +4,7 @@ exports.up = function (knex) {
|
||||
table.increments();
|
||||
table.string('name');
|
||||
table.string('type');
|
||||
table.string('sku');
|
||||
table.decimal('cost_price').unsigned();
|
||||
table.decimal('sell_price').unsigned();
|
||||
table.string('currency_code', 3);
|
||||
@@ -15,7 +16,7 @@ exports.up = function (knex) {
|
||||
table.integer('category_id').unsigned();
|
||||
table.integer('user_id').unsigned();
|
||||
table.timestamps();
|
||||
});
|
||||
}).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000');;
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('items');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('accounts', (table) => {
|
||||
table.increments();
|
||||
table.bigIncrements('id').comment('Auto-generated id');;
|
||||
table.string('name');
|
||||
table.integer('account_type_id');
|
||||
table.integer('parent_account_id');
|
||||
@@ -11,7 +11,7 @@ exports.up = function (knex) {
|
||||
table.integer('index').unsigned();
|
||||
table.boolean('predefined').defaultTo(false);
|
||||
table.timestamps();
|
||||
}).then(() => {
|
||||
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_accounts.js',
|
||||
});
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('items_metadata', (table) => {
|
||||
table.increments();
|
||||
table.string('key');
|
||||
table.string('value');
|
||||
table.integer('item_id').unsigned();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('items_metadata');
|
||||
@@ -7,7 +7,7 @@ exports.up = (knex) => {
|
||||
table.string('root_type');
|
||||
table.boolean('balance_sheet');
|
||||
table.boolean('income_sheet');
|
||||
}).then(() => {
|
||||
}).raw('ALTER TABLE `ACCOUNT_TYPES` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_account_types.js',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ exports.up = function (knex) {
|
||||
table.integer('index');
|
||||
table.json('options');
|
||||
table.integer('resource_id').unsigned();
|
||||
}).then(() => {
|
||||
}).raw('ALTER TABLE `RESOURCE_FIELDS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_resources_fields.js',
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ exports.up = function (knex) {
|
||||
table.integer('view_id').unsigned();
|
||||
table.integer('field_id').unsigned();
|
||||
table.integer('index').unsigned();
|
||||
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000').then(() => {
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ exports.up = function (knex) {
|
||||
table.string('comparator');
|
||||
table.string('value');
|
||||
table.integer('view_id').unsigned();
|
||||
}).then(() => {
|
||||
}).raw('ALTER TABLE `VIEW_ROLES` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_views_roles.js',
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ exports.up = function (knex) {
|
||||
table.integer('resource_id').unsigned().references('id').inTable('resources');
|
||||
table.boolean('favourite');
|
||||
table.string('roles_logic_expression');
|
||||
}).then(() => {
|
||||
}).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_views.js',
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ exports.up = function(knex) {
|
||||
table.integer('user_id').unsigned();
|
||||
table.date('date');
|
||||
table.timestamps();
|
||||
});
|
||||
}).raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
@@ -12,7 +12,7 @@ exports.up = function(knex) {
|
||||
table.string('attachment_file');
|
||||
table.integer('user_id').unsigned();
|
||||
table.timestamps();
|
||||
});
|
||||
}).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
@@ -4,7 +4,7 @@ exports.up = function(knex) {
|
||||
table.increments();
|
||||
table.string('currency_name');
|
||||
table.string('currency_code', 4);
|
||||
})
|
||||
}).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
@@ -5,7 +5,7 @@ exports.up = function(knex) {
|
||||
table.string('currency_code', 4);
|
||||
table.decimal('exchange_rate');
|
||||
table.date('date');
|
||||
});
|
||||
}).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
@@ -68,6 +68,10 @@ export default {
|
||||
this.transferToAnotherAccount.validation,
|
||||
asyncMiddleware(this.transferToAnotherAccount.handler));
|
||||
|
||||
router.post('/bulk/:type(activate|inactivate)',
|
||||
this.bulkInactivateAccounts.validation,
|
||||
asyncMiddleware(this.bulkInactivateAccounts.handler));
|
||||
|
||||
return router;
|
||||
},
|
||||
|
||||
@@ -541,4 +545,47 @@ export default {
|
||||
return res.status(200).send();
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Bulk acvtivate/inactivate the given accounts.
|
||||
*/
|
||||
bulkInactivateAccounts: {
|
||||
validation: [
|
||||
query('ids').isArray({ min: 2 }),
|
||||
query('ids.*').isNumeric().toInt(),
|
||||
param('type').exists().isIn(['activate', 'inactivate']),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const filter = {
|
||||
ids: [],
|
||||
...req.query,
|
||||
};
|
||||
const { Account } = req.models;
|
||||
const { type } = req.params;
|
||||
|
||||
const storedAccounts = await Account.query().whereIn('id', filter.ids);
|
||||
const storedAccountsIds = storedAccounts.map((account) => account.id);
|
||||
const notFoundAccounts = difference(filter.ids, storedAccountsIds);
|
||||
|
||||
if (notFoundAccounts.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'ACCOUNTS.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
const updatedAccounts = await Account.query()
|
||||
.whereIn('id', storedAccountsIds)
|
||||
.patch({
|
||||
active: type === 'activate' ? 1 : 0,
|
||||
});
|
||||
|
||||
return res.status(200).send({ ids: storedAccountsIds });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import Mustache from 'mustache';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { pick } from 'lodash';
|
||||
import uniqid from 'uniqid';
|
||||
import moment from 'moment';
|
||||
import Logger from '@/services/Logger';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import SystemUser from '@/system/models/SystemUser';
|
||||
@@ -19,6 +20,7 @@ import TenantsManager from '@/system/TenantsManager';
|
||||
import TenantModel from '@/models/TenantModel';
|
||||
import PasswordReset from '@/system/models/PasswordReset';
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -51,7 +53,7 @@ export default {
|
||||
login: {
|
||||
validation: [
|
||||
check('crediential').exists().isEmail(),
|
||||
check('password').exists().isLength({ min: 4 }),
|
||||
check('password').exists().isLength({ min: 5 }),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
@@ -87,7 +89,17 @@ export default {
|
||||
errors: [{ type: 'USER_INACTIVE', code: 110 }],
|
||||
});
|
||||
}
|
||||
// user.update({ last_login_at: new Date() });
|
||||
const lastLoginAt = moment().format('YYYY/MM/DD HH:mm:ss');
|
||||
|
||||
const updateTenantUser = TenantUser.tenant().query()
|
||||
.where('id', user.id)
|
||||
.update({ last_login_at: lastLoginAt });
|
||||
|
||||
const updateSystemUser = SystemUser.query()
|
||||
.where('id', user.id)
|
||||
.update({ last_login_at: lastLoginAt });
|
||||
|
||||
await Promise.all([updateTenantUser, updateSystemUser]);
|
||||
|
||||
const token = jwt.sign(
|
||||
{ email: user.email, _id: user.id },
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
validationResult,
|
||||
} from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
|
||||
export default {
|
||||
@@ -27,6 +28,10 @@ export default {
|
||||
this.editExchangeRate.validation,
|
||||
asyncMiddleware(this.editExchangeRate.handler));
|
||||
|
||||
router.delete('/bulk',
|
||||
this.bulkDeleteExchangeRates.validation,
|
||||
asyncMiddleware(this.bulkDeleteExchangeRates.handler));
|
||||
|
||||
router.delete('/:id',
|
||||
this.deleteExchangeRate.validation,
|
||||
asyncMiddleware(this.deleteExchangeRate.handler));
|
||||
@@ -166,4 +171,39 @@ export default {
|
||||
return res.status(200).send({ id });
|
||||
},
|
||||
},
|
||||
|
||||
bulkDeleteExchangeRates: {
|
||||
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 = {
|
||||
ids: [],
|
||||
...req.query,
|
||||
};
|
||||
const { ExchangeRate } = req.models;
|
||||
|
||||
const exchangeRates = await ExchangeRate.query().whereIn('id', filter.ids);
|
||||
const exchangeRatesIds = exchangeRates.map((category) => category.id);
|
||||
const notFoundExRates = difference(filter.ids, exchangeRatesIds);
|
||||
|
||||
if (notFoundExRates.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXCHANGE.RATES.IS.NOT.FOUND', code: 200, ids: notFoundExRates }],
|
||||
});
|
||||
}
|
||||
await ExchangeRate.query().whereIn('id', exchangeRatesIds).delete();
|
||||
|
||||
return res.status(200).send({ ids: exchangeRatesIds });
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
validationResult,
|
||||
query,
|
||||
} from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '../middleware/asyncMiddleware';
|
||||
import {
|
||||
DynamicFilter,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
mapFilterRolesToDynamicFilter,
|
||||
} from '@/lib/ViewRolesBuilder';
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Router constructor method.
|
||||
@@ -31,6 +33,10 @@ export default {
|
||||
this.newCategory.validation,
|
||||
asyncMiddleware(this.newCategory.handler));
|
||||
|
||||
router.delete('/bulk',
|
||||
this.bulkDeleteCategories.validation,
|
||||
asyncMiddleware(this.bulkDeleteCategories.handler));
|
||||
|
||||
router.delete('/:id',
|
||||
this.deleteItem.validation,
|
||||
asyncMiddleware(this.deleteItem.handler));
|
||||
@@ -43,6 +49,8 @@ export default {
|
||||
this.getList.validation,
|
||||
asyncMiddleware(this.getList.handler));
|
||||
|
||||
|
||||
|
||||
return router;
|
||||
},
|
||||
|
||||
@@ -145,7 +153,7 @@ export default {
|
||||
.where('id', id)
|
||||
.update({ ...form });
|
||||
|
||||
return res.status(200).send({ id: updateItemCategory });
|
||||
return res.status(200).send({ id });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -274,8 +282,46 @@ export default {
|
||||
errors: [{ type: 'CATEGORY_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({ category: item.toJSON() });
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Bulk delete the given item categories.
|
||||
*/
|
||||
bulkDeleteCategories: {
|
||||
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 = {
|
||||
ids: [],
|
||||
...req.query,
|
||||
};
|
||||
const { ItemCategory } = req.models;
|
||||
|
||||
const itemCategories = await ItemCategory.query().whereIn('id', filter.ids);
|
||||
const itemCategoriesIds = itemCategories.map((category) => category.id);
|
||||
const notFoundCategories = difference(filter.ids, itemCategoriesIds);
|
||||
|
||||
if (notFoundCategories.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'ITEM.CATEGORIES.IDS.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
|
||||
await ItemCategory.query().whereIn('id', filter.ids).delete();
|
||||
|
||||
return res.status(200).send({ ids: filter.ids });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import Logger from '@/services/Logger';
|
||||
|
||||
const fsPromises = fs.promises;
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -80,6 +81,7 @@ export default {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const { user } = req;
|
||||
const form = {
|
||||
custom_fields: [],
|
||||
media_ids: [],
|
||||
@@ -129,8 +131,10 @@ export default {
|
||||
itemCategory,
|
||||
inventoryAccount,
|
||||
] = await Promise.all([
|
||||
costAccountPromise, sellAccountPromise,
|
||||
itemCategoryPromise, inventoryAccountPromise,
|
||||
costAccountPromise,
|
||||
sellAccountPromise,
|
||||
itemCategoryPromise,
|
||||
inventoryAccountPromise,
|
||||
]);
|
||||
if (!costAccount) {
|
||||
errorReasons.push({ type: 'COST_ACCOUNT_NOT_FOUND', code: 100 });
|
||||
@@ -152,11 +156,14 @@ export default {
|
||||
const item = await Item.query().insertAndFetch({
|
||||
name: form.name,
|
||||
type: form.type,
|
||||
sku: form.sku,
|
||||
cost_price: form.cost_price,
|
||||
sell_price: form.sell_price,
|
||||
sell_account_id: form.sell_account_id,
|
||||
cost_account_id: form.cost_account_id,
|
||||
currency_code: form.currency_code,
|
||||
category_id: form.category_id,
|
||||
user_id: user.id,
|
||||
note: form.note,
|
||||
});
|
||||
|
||||
@@ -239,7 +246,7 @@ export default {
|
||||
errorReasons.push({ type: 'ITEM_CATEGORY_NOT_FOUND', code: 140 });
|
||||
}
|
||||
|
||||
const { attachment } = req.files;
|
||||
const attachment = req.files && req.files.attachment ? req.files.attachment : null;
|
||||
const attachmentsMimes = ['image/png', 'image/jpeg'];
|
||||
|
||||
// Validate the attachment.
|
||||
@@ -277,7 +284,6 @@ export default {
|
||||
cost_account_id: form.cost_account_id,
|
||||
category_id: form.category_id,
|
||||
note: form.note,
|
||||
attachment_file: (attachment) ? item.attachmentFile : null,
|
||||
});
|
||||
|
||||
// Save links of new inserted media that associated to the item model.
|
||||
|
||||
55
server/src/lib/KnexFactory/index.js
Normal file
55
server/src/lib/KnexFactory/index.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const { extend, isFunction, isObject } = require('lodash');
|
||||
|
||||
export default class KnexFactory {
|
||||
|
||||
constructor(knex) {
|
||||
this.knex = knex;
|
||||
|
||||
this.factories = [];
|
||||
}
|
||||
|
||||
define(name, tableName, defaultAttributes) {
|
||||
this.factories[name] = { tableName, defaultAttributes };
|
||||
}
|
||||
|
||||
async build(factoryName, attributes) {
|
||||
const factory = this.factories[factoryName];
|
||||
|
||||
if (!factory) {
|
||||
throw `Unkown factory: ${factoryName}`;
|
||||
}
|
||||
let { defaultAttributes } = factory;
|
||||
const insertData = {};
|
||||
|
||||
if( 'function' === typeof defaultAttributes) {
|
||||
defaultAttributes = await defaultAttributes();
|
||||
}
|
||||
extend(insertData, defaultAttributes, attributes);
|
||||
|
||||
for (let k in insertData) {
|
||||
const v = insertData[k];
|
||||
|
||||
if (isFunction(v)) {
|
||||
insertData[k] = await v();
|
||||
} else {
|
||||
insertData[k] = await v;
|
||||
}
|
||||
if (isObject(insertData[k]) && insertData[k].id) {
|
||||
insertData[k] = insertData[k].id;
|
||||
}
|
||||
};
|
||||
|
||||
return insertData;
|
||||
}
|
||||
|
||||
async create(factoryName, attributes) {
|
||||
const factory = this.factories[factoryName];
|
||||
const insertData = await this.build(factoryName, attributes);
|
||||
const { tableName } = factory;
|
||||
|
||||
const [id] = await this.knex(tableName).insert(insertData);
|
||||
const record = await this.knex(tableName).where({ id }).first();
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,16 @@ export default class Option extends mixin(TenantModel, [mixin]) {
|
||||
static query(...args) {
|
||||
return super.query(...args).runAfter((result) => {
|
||||
if (result instanceof MetableCollection) {
|
||||
result.setModel(Option);
|
||||
result.setModel(this.tenant());
|
||||
result.setExtraColumns(['group']);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Model collection.
|
||||
*/
|
||||
static get collection() {
|
||||
return MetableCollection;
|
||||
}
|
||||
|
||||
12
server/src/system/TenantEnvironment.js
Normal file
12
server/src/system/TenantEnvironment.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
export default class TenantEnviroment {
|
||||
|
||||
static get currentTenant() {
|
||||
return this.currentTenantWebsite;
|
||||
}
|
||||
|
||||
static set currentTenant(website) {
|
||||
this.currentTenantWebsite = website;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,20 @@ import Knex from 'knex';
|
||||
import { knexSnakeCaseMappers } from 'objection';
|
||||
import Tenant from '@/system/models/Tenant';
|
||||
import config from '@/../config/config';
|
||||
import TenantModel from '@/models/TenantModel';
|
||||
import uniqid from 'uniqid';
|
||||
import dbManager from '@/database/manager';
|
||||
import { omit } from 'lodash';
|
||||
|
||||
import SystemUser from '@/system/models/SystemUser';
|
||||
import TenantUser from '@/models/TenantUser';
|
||||
// import TenantModel from '@/models/TenantModel';
|
||||
|
||||
// const TenantWebsite: {
|
||||
// tenantDb: Knex,
|
||||
// tenantId: Number,
|
||||
// tenantOrganizationId: String,
|
||||
// }
|
||||
|
||||
export default class TenantsManager {
|
||||
|
||||
@@ -16,6 +30,65 @@ export default class TenantsManager {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tenant database.
|
||||
* @param {Integer} uniqId
|
||||
* @return {TenantWebsite}
|
||||
*/
|
||||
static async createTenant(uniqId) {
|
||||
const organizationId = uniqId || uniqid();
|
||||
const tenantOrganization = await Tenant.query().insert({
|
||||
organization_id: organizationId,
|
||||
});
|
||||
|
||||
const tenantDbName = `bigcapital_tenant_${organizationId}`;
|
||||
await dbManager.createDb(tenantDbName);
|
||||
|
||||
const tenantDb = TenantsManager.knexInstance(organizationId);
|
||||
await tenantDb.migrate.latest();
|
||||
|
||||
return {
|
||||
tenantDb,
|
||||
tenantId: tenantOrganization.id,
|
||||
organizationId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop tenant database of the given tenant website.
|
||||
* @param {TenantWebsite} tenantWebsite
|
||||
*/
|
||||
static async dropTenant(tenantWebsite) {
|
||||
const tenantDbName = `bigcapital_tenant_${tenantWebsite.organizationId}`;
|
||||
await dbManager.dropDb(tenantDbName);
|
||||
|
||||
await SystemUser.query()
|
||||
.where('tenant_id', tenantWebsite.tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user that associate to the given tenant.
|
||||
*/
|
||||
static async createTenantUser(tenantWebsite, user) {
|
||||
const userInsert = { ...user };
|
||||
|
||||
const systemUser = await SystemUser.query().insert({
|
||||
...user,
|
||||
tenant_id: tenantWebsite.tenantId,
|
||||
});
|
||||
TenantModel.knexBinded = tenantWebsite.tenantDb;
|
||||
|
||||
const tenantUser = await TenantUser.bindKnex(tenantWebsite.tenantDb)
|
||||
.query()
|
||||
.insert({
|
||||
...omit(userInsert, ['password']),
|
||||
});
|
||||
return {
|
||||
...tenantUser,
|
||||
...systemUser
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all tenants metadata from system storage.
|
||||
*/
|
||||
@@ -43,6 +116,7 @@ export default class TenantsManager {
|
||||
seeds: {
|
||||
directory: config.tenant.seeds_dir,
|
||||
},
|
||||
pool: { min: 0, max: 5 },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user