mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
feat: ensure organization tenant configured.
This commit is contained in:
20
README.md
20
README.md
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
CLIENT
|
|
||||||
------------------------
|
|
||||||
1. `cd client`
|
|
||||||
2. `npm install`
|
|
||||||
|
|
||||||
RUN CLINET
|
|
||||||
1. npm run start
|
|
||||||
|
|
||||||
SERVER
|
|
||||||
-----------------------
|
|
||||||
1. cd server
|
|
||||||
2. npm install
|
|
||||||
3. npm install -g knex webpack
|
|
||||||
4. write database details to .env files.
|
|
||||||
5. `knex migrate:latest`
|
|
||||||
6. `knex seed:run`
|
|
||||||
|
|
||||||
RUN SERVER
|
|
||||||
1. npm run start
|
|
||||||
@@ -8,6 +8,8 @@ import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
|
|||||||
import OrganizationService from 'services/Organization';
|
import OrganizationService from 'services/Organization';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
|
import EnsureConfiguredMiddleware from 'api/middleware/EnsureConfiguredMiddleware';
|
||||||
|
import SettingsMiddleware from 'api/middleware/SettingsMiddleware';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class OrganizationController extends BaseController{
|
export default class OrganizationController extends BaseController{
|
||||||
@@ -27,6 +29,10 @@ export default class OrganizationController extends BaseController{
|
|||||||
router.use(TenancyMiddleware);
|
router.use(TenancyMiddleware);
|
||||||
router.use(SubscriptionMiddleware('main'));
|
router.use(SubscriptionMiddleware('main'));
|
||||||
|
|
||||||
|
// Should to seed organization tenant be configured.
|
||||||
|
router.use('/seed', SettingsMiddleware);
|
||||||
|
router.use('/seed', EnsureConfiguredMiddleware);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/build',
|
'/build',
|
||||||
asyncMiddleware(this.build.bind(this))
|
asyncMiddleware(this.build.bind(this))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import express from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { check, param, query, matchedData } from 'express-validator';
|
import { check, param, query, matchedData } from 'express-validator';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import { raw } from 'objection';
|
import { raw } from 'objection';
|
||||||
@@ -21,7 +21,7 @@ export default class SaleInvoicesController {
|
|||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
router() {
|
router() {
|
||||||
const router = express.Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
|
|||||||
@@ -62,9 +62,7 @@ export default class SettingsController extends BaseController{
|
|||||||
errorReasons.push({
|
errorReasons.push({
|
||||||
type: 'OPTIONS.KEY.NOT.DEFINED',
|
type: 'OPTIONS.KEY.NOT.DEFINED',
|
||||||
code: 200,
|
code: 200,
|
||||||
keys: notDefinedOptions.map((o) => ({
|
keys: notDefinedOptions.map((o) => ({ ...pick(o, ['key', 'group']) })),
|
||||||
...pick(o, ['key', 'group'])
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (errorReasons.length) {
|
if (errorReasons.length) {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
|
|||||||
import EnsureTenantIsInitialized from 'api/middleware/EnsureTenantIsInitialized';
|
import EnsureTenantIsInitialized from 'api/middleware/EnsureTenantIsInitialized';
|
||||||
import SettingsMiddleware from 'api/middleware/SettingsMiddleware';
|
import SettingsMiddleware from 'api/middleware/SettingsMiddleware';
|
||||||
import I18nMiddleware from 'api/middleware/I18nMiddleware';
|
import I18nMiddleware from 'api/middleware/I18nMiddleware';
|
||||||
|
import EnsureConfiguredMiddleware from 'api/middleware/EnsureConfiguredMiddleware';
|
||||||
|
import EnsureTenantIsSeeded from 'api/middleware/EnsureTenantIsSeeded';
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
import Authentication from 'api/controllers/Authentication';
|
import Authentication from 'api/controllers/Authentication';
|
||||||
@@ -57,6 +59,8 @@ export default () => {
|
|||||||
dashboard.use(SubscriptionMiddleware('main'));
|
dashboard.use(SubscriptionMiddleware('main'));
|
||||||
dashboard.use(EnsureTenantIsInitialized);
|
dashboard.use(EnsureTenantIsInitialized);
|
||||||
dashboard.use(SettingsMiddleware);
|
dashboard.use(SettingsMiddleware);
|
||||||
|
dashboard.use(EnsureConfiguredMiddleware);
|
||||||
|
dashboard.use(EnsureTenantIsSeeded);
|
||||||
|
|
||||||
dashboard.use('/users', Container.get(Users).router());
|
dashboard.use('/users', Container.get(Users).router());
|
||||||
dashboard.use('/invite', Container.get(InviteUsers).authRouter());
|
dashboard.use('/invite', Container.get(InviteUsers).authRouter());
|
||||||
@@ -76,7 +80,7 @@ export default () => {
|
|||||||
dashboard.use('/purchases', Purchases.router());
|
dashboard.use('/purchases', Purchases.router());
|
||||||
dashboard.use('/resources', Resources.router());
|
dashboard.use('/resources', Resources.router());
|
||||||
dashboard.use('/exchange_rates', Container.get(ExchangeRates).router());
|
dashboard.use('/exchange_rates', Container.get(ExchangeRates).router());
|
||||||
dashboard.use('/media', Media.router())
|
dashboard.use('/media', Media.router());
|
||||||
|
|
||||||
app.use('/', dashboard);
|
app.use('/', dashboard);
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Container } from 'typedi';
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
|
||||||
|
|
||||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
|
||||||
const { Option } = req.models;
|
|
||||||
const option = await Option.query().where('key', 'app_configured');
|
|
||||||
|
|
||||||
if (option.getMeta('app_configured', false)) {
|
|
||||||
return res.res(400).send({
|
|
||||||
errors: [{ type: 'TENANT.NOT.CONFIGURED', code: 700 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
12
server/src/api/middleware/EnsureConfiguredMiddleware.ts
Normal file
12
server/src/api/middleware/EnsureConfiguredMiddleware.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
export default (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const { settings } = req;
|
||||||
|
|
||||||
|
if (!settings.get('app_configured', false)) {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'APP.NOT.CONFIGURED', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -16,12 +16,5 @@ export default (req: Request, res: Response, next: Function) => {
|
|||||||
{ errors: [{ type: 'TENANT.DATABASE.NOT.INITALIZED' }] },
|
{ errors: [{ type: 'TENANT.DATABASE.NOT.INITALIZED' }] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!req.tenant.seededAt) {
|
|
||||||
Logger.info('[ensure_tenant_initialized_middleware] tenant databae not seeded.');
|
|
||||||
return res.boom.badRequest(
|
|
||||||
'Tenant database is not seeded with initial data yet.',
|
|
||||||
{ errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
19
server/src/api/middleware/EnsureTenantIsSeeded.ts
Normal file
19
server/src/api/middleware/EnsureTenantIsSeeded.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
export default (req: Request, res: Response, next: Function) => {
|
||||||
|
const Logger = Container.get('logger');
|
||||||
|
|
||||||
|
if (!req.tenant) {
|
||||||
|
Logger.info('[ensure_tenant_intialized_middleware] no tenant model.');
|
||||||
|
throw new Error('Should load this middleware after `TenancyMiddleware`.');
|
||||||
|
}
|
||||||
|
if (!req.tenant.seededAt) {
|
||||||
|
Logger.info('[ensure_tenant_initialized_middleware] tenant databae not seeded.');
|
||||||
|
return res.boom.badRequest(
|
||||||
|
'Tenant database is not seeded with initial data yet.',
|
||||||
|
{ errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -5,10 +5,12 @@ export default {
|
|||||||
{
|
{
|
||||||
key: 'name',
|
key: 'name',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'base_currency',
|
key: 'base_currency',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'industry',
|
key: 'industry',
|
||||||
@@ -21,18 +23,22 @@ export default {
|
|||||||
{
|
{
|
||||||
key: 'fiscal_year',
|
key: 'fiscal_year',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'language',
|
key: 'language',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'time_zone',
|
key: 'time_zone',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'date_format',
|
key: 'date_format',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
configure: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
|
import { ISystemUser } from './User';
|
||||||
|
import { ITenant } from './Tenancy';
|
||||||
|
|
||||||
export interface IRegisterDTO {
|
export interface IRegisterDTO {
|
||||||
firstName: string,
|
firstName: string,
|
||||||
@@ -14,3 +15,10 @@ export interface IPasswordReset {
|
|||||||
token: string,
|
token: string,
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IAuthenticationService {
|
||||||
|
signIn(emailOrPhone: string, password: string): Promise<{ user: ISystemUser, token: string, tenant: ITenant }>;
|
||||||
|
register(registerDTO: IRegisterDTO): Promise<ISystemUser>;
|
||||||
|
sendResetPassword(email: string): Promise<IPasswordReset>;
|
||||||
|
resetPassword(token: string, password: string): Promise<void>;
|
||||||
|
}
|
||||||
@@ -14,6 +14,10 @@ export interface ISystemUser {
|
|||||||
|
|
||||||
inviteAcceptAt: Date,
|
inviteAcceptAt: Date,
|
||||||
lastLoginAt: Date,
|
lastLoginAt: Date,
|
||||||
|
deletedAt: Date,
|
||||||
|
|
||||||
|
createdAt: Date,
|
||||||
|
updatedAt: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISystemUserDTO {
|
export interface ISystemUserDTO {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export interface IView {
|
|||||||
id: number,
|
id: number,
|
||||||
name: string,
|
name: string,
|
||||||
predefined: boolean,
|
predefined: boolean,
|
||||||
resourceId: number,
|
resourceModel: string,
|
||||||
favourite: boolean,
|
favourite: boolean,
|
||||||
rolesLogicRxpression: string,
|
rolesLogicRxpression: string,
|
||||||
};
|
};
|
||||||
@@ -18,7 +18,44 @@ export interface IViewRole {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface IViewHasColumn {
|
export interface IViewHasColumn {
|
||||||
|
id :number,
|
||||||
viewId: number,
|
viewId: number,
|
||||||
fieldId: number,
|
fieldId: number,
|
||||||
index: number,
|
index: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IViewRoleDTO {
|
||||||
|
index: number,
|
||||||
|
fieldKey: string,
|
||||||
|
comparator: string,
|
||||||
|
value: string,
|
||||||
|
viewId: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IViewColumnDTO {
|
||||||
|
id: number,
|
||||||
|
index: number,
|
||||||
|
viewId: number,
|
||||||
|
fieldKey: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IViewDTO {
|
||||||
|
name: string,
|
||||||
|
logicExpression: string,
|
||||||
|
roles: IViewRoleDTO[],
|
||||||
|
columns: IViewColumnDTO[],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IViewEditDTO {
|
||||||
|
name: string,
|
||||||
|
logicExpression: string,
|
||||||
|
roles: IViewRoleDTO[],
|
||||||
|
columns: IViewColumnDTO[],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IViewsService {
|
||||||
|
listViews(tenantId: number, resourceModel: string): Promise<void>;
|
||||||
|
newView(tenantId: number, viewDTO: IViewDTO): Promise<void>;
|
||||||
|
editView(tenantId: number, viewId: number, viewEditDTO: IViewEditDTO): Promise<void>;
|
||||||
|
deleteView(tenantId: number, viewId: number): Promise<void>;
|
||||||
|
}
|
||||||
@@ -31,6 +31,9 @@ export default class ViewRepository extends TenantRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all views of the given resource id.
|
||||||
|
*/
|
||||||
allByResource() {
|
allByResource() {
|
||||||
const resourceId = 1;
|
const resourceId = 1;
|
||||||
return this.cache.get(`customView.resource.id.${resourceId}`, () => {
|
return this.cache.get(`customView.resource.id.${resourceId}`, () => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import AuthenticationSMSMessages from 'services/Authentication/AuthenticationSMS
|
|||||||
import TenantsManager from 'services/Tenancy/TenantsManager';
|
import TenantsManager from 'services/Tenancy/TenantsManager';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationService {
|
export default class AuthenticationService implements IAuthenticationService {
|
||||||
@Inject('logger')
|
@Inject('logger')
|
||||||
logger: any;
|
logger: any;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export default class AuthenticationService {
|
|||||||
* @param {string} password - Password.
|
* @param {string} password - Password.
|
||||||
* @return {Promise<{user: IUser, token: string}>}
|
* @return {Promise<{user: IUser, token: string}>}
|
||||||
*/
|
*/
|
||||||
async signIn(emailOrPhone: string, password: string): Promise<{user: ISystemUser, token: string, tenant: ITenant }> {
|
public async signIn(emailOrPhone: string, password: string): Promise<{user: ISystemUser, token: string, tenant: ITenant }> {
|
||||||
this.logger.info('[login] Someone trying to login.', { emailOrPhone, password });
|
this.logger.info('[login] Someone trying to login.', { emailOrPhone, password });
|
||||||
|
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
@@ -122,7 +122,7 @@ export default class AuthenticationService {
|
|||||||
* @throws {ServiceErrors}
|
* @throws {ServiceErrors}
|
||||||
* @param {IUserDTO} user
|
* @param {IUserDTO} user
|
||||||
*/
|
*/
|
||||||
async register(registerDTO: IRegisterDTO): Promise<ISystemUser> {
|
public async register(registerDTO: IRegisterDTO): Promise<ISystemUser> {
|
||||||
this.logger.info('[register] Someone trying to register.');
|
this.logger.info('[register] Someone trying to register.');
|
||||||
await this.validateEmailAndPhoneUniqiness(registerDTO);
|
await this.validateEmailAndPhoneUniqiness(registerDTO);
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ export default class AuthenticationService {
|
|||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
* @param {string} email - email address.
|
* @param {string} email - email address.
|
||||||
*/
|
*/
|
||||||
private async validateEmailExistance(email: string) {
|
private async validateEmailExistance(email: string): Promise<ISystemUser> {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
const userByEmail = await systemUserRepository.getByEmail(email);
|
const userByEmail = await systemUserRepository.getByEmail(email);
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ export default class AuthenticationService {
|
|||||||
* @param {string} email
|
* @param {string} email
|
||||||
* @return {<Promise<IPasswordReset>}
|
* @return {<Promise<IPasswordReset>}
|
||||||
*/
|
*/
|
||||||
async sendResetPassword(email: string): Promise<IPasswordReset> {
|
public async sendResetPassword(email: string): Promise<IPasswordReset> {
|
||||||
this.logger.info('[send_reset_password] Trying to send reset password.');
|
this.logger.info('[send_reset_password] Trying to send reset password.');
|
||||||
const user = await this.validateEmailExistance(email);
|
const user = await this.validateEmailExistance(email);
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ export default class AuthenticationService {
|
|||||||
this.logger.info('[send_reset_password] trying to delete all tokens by email.');
|
this.logger.info('[send_reset_password] trying to delete all tokens by email.');
|
||||||
this.deletePasswordResetToken(email);
|
this.deletePasswordResetToken(email);
|
||||||
|
|
||||||
const token = uniqid();
|
const token: string = uniqid();
|
||||||
|
|
||||||
this.logger.info('[send_reset_password] insert the generated token.');
|
this.logger.info('[send_reset_password] insert the generated token.');
|
||||||
const passwordReset = await PasswordReset.query().insert({ email, token });
|
const passwordReset = await PasswordReset.query().insert({ email, token });
|
||||||
@@ -201,9 +201,9 @@ export default class AuthenticationService {
|
|||||||
* @param {string} password - New Password.
|
* @param {string} password - New Password.
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async resetPassword(token: string, password: string): Promise<void> {
|
public async resetPassword(token: string, password: string): Promise<void> {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
const tokenModel = await PasswordReset.query().findOne('token', token);
|
const tokenModel: IPasswordReset = await PasswordReset.query().findOne('token', token);
|
||||||
|
|
||||||
if (!tokenModel) {
|
if (!tokenModel) {
|
||||||
this.logger.info('[reset_password] token invalid.');
|
this.logger.info('[reset_password] token invalid.');
|
||||||
|
|||||||
@@ -7,7 +7,14 @@ export default class SMSAPI {
|
|||||||
this.smsClient = smsClient;
|
this.smsClient = smsClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(to: string, message: string, extraParams: [], extraHeaders: []) {
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} to
|
||||||
|
* @param {string} message
|
||||||
|
* @param {array} extraParams
|
||||||
|
* @param {array} extraHeaders
|
||||||
|
*/
|
||||||
|
sendMessage(to: string, message: string, extraParams?: [], extraHeaders?: []) {
|
||||||
return this.smsClient.send(to, message);
|
return this.smsClient.send(to, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,12 @@ export default class SubscriptionSMSMessages {
|
|||||||
@Inject('SMSClient')
|
@Inject('SMSClient')
|
||||||
smsClient: SMSClient;
|
smsClient: SMSClient;
|
||||||
|
|
||||||
public async sendRemainingSubscriptionPeriod(phoneNumber: string, remainingDays: number) {
|
/**
|
||||||
|
* Send remaining subscription period SMS message.
|
||||||
|
* @param {string} phoneNumber -
|
||||||
|
* @param {number} remainingDays -
|
||||||
|
*/
|
||||||
|
public async sendRemainingSubscriptionPeriod(phoneNumber: string, remainingDays: number): Promise<void> {
|
||||||
const message: string = `
|
const message: string = `
|
||||||
Your remaining subscription is ${remainingDays} days,
|
Your remaining subscription is ${remainingDays} days,
|
||||||
please renew your subscription before expire.
|
please renew your subscription before expire.
|
||||||
@@ -14,7 +19,12 @@ export default class SubscriptionSMSMessages {
|
|||||||
this.smsClient.sendMessage(phoneNumber, message);
|
this.smsClient.sendMessage(phoneNumber, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendRemainingTrialPeriod(phoneNumber: string, remainingDays: number) {
|
/**
|
||||||
|
* Send remaining trial period SMS message.
|
||||||
|
* @param {string} phoneNumber -
|
||||||
|
* @param {number} remainingDays -
|
||||||
|
*/
|
||||||
|
public async sendRemainingTrialPeriod(phoneNumber: string, remainingDays: number): Promise<void> {
|
||||||
const message: string = `
|
const message: string = `
|
||||||
Your remaining free trial is ${remainingDays} days,
|
Your remaining free trial is ${remainingDays} days,
|
||||||
please subscription before ends, if you have any quation to contact us.`;
|
please subscription before ends, if you have any quation to contact us.`;
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ exports.up = function(knex) {
|
|||||||
table.integer('license_period').unsigned();
|
table.integer('license_period').unsigned();
|
||||||
table.string('period_interval');
|
table.string('period_interval');
|
||||||
|
|
||||||
table.boolean('sent').defaultTo(false);
|
|
||||||
table.boolean('disabled').defaultTo(false);
|
|
||||||
table.boolean('used').defaultTo(false);
|
|
||||||
|
|
||||||
table.dateTime('sent_at');
|
table.dateTime('sent_at');
|
||||||
table.dateTime('disabled_at');
|
table.dateTime('disabled_at');
|
||||||
table.dateTime('used_at');
|
table.dateTime('used_at');
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Model, mixin } from 'objection';
|
import { Model, mixin } from 'objection';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import SystemModel from 'system/models/SystemModel';
|
import SystemModel from 'system/models/SystemModel';
|
||||||
import { ILicensesFilter } from 'interfaces';
|
|
||||||
|
|
||||||
export default class License extends SystemModel {
|
export default class License extends SystemModel {
|
||||||
/**
|
/**
|
||||||
@@ -25,8 +24,8 @@ export default class License extends SystemModel {
|
|||||||
return {
|
return {
|
||||||
// Filters active licenses.
|
// Filters active licenses.
|
||||||
filterActiveLicense(query) {
|
filterActiveLicense(query) {
|
||||||
query.where('disabled', false);
|
query.where('disabled_at', null);
|
||||||
query.where('used', false);
|
query.where('used_at', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Find license by its code or id.
|
// Find license by its code or id.
|
||||||
@@ -45,13 +44,13 @@ export default class License extends SystemModel {
|
|||||||
builder.modify('filterActiveLicense')
|
builder.modify('filterActiveLicense')
|
||||||
}
|
}
|
||||||
if (licensesFilter.disabled) {
|
if (licensesFilter.disabled) {
|
||||||
builder.where('disabled', true);
|
builder.whereNot('disabled_at', null);
|
||||||
}
|
}
|
||||||
if (licensesFilter.used) {
|
if (licensesFilter.used) {
|
||||||
builder.where('used', true);
|
builder.whereNot('used_at', null);
|
||||||
}
|
}
|
||||||
if (licensesFilter.sent) {
|
if (licensesFilter.sent) {
|
||||||
builder.where('sent', true);
|
builder.whereNot('sent_at', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -95,7 +94,6 @@ export default class License extends SystemModel {
|
|||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, licenseCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
disabled: true,
|
|
||||||
disabled_at: moment().toMySqlDateTime(),
|
disabled_at: moment().toMySqlDateTime(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -108,7 +106,6 @@ export default class License extends SystemModel {
|
|||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, licenseCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
sent: true,
|
|
||||||
sent_at: moment().toMySqlDateTime(),
|
sent_at: moment().toMySqlDateTime(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -122,7 +119,6 @@ export default class License extends SystemModel {
|
|||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, licenseCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
used: true,
|
|
||||||
used_at: moment().toMySqlDateTime()
|
used_at: moment().toMySqlDateTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -136,5 +132,4 @@ export default class License extends SystemModel {
|
|||||||
return (this.invoicePeriod === plan.invoiceInterval &&
|
return (this.invoicePeriod === plan.invoiceInterval &&
|
||||||
license.licensePeriod === license.periodInterval);
|
license.licensePeriod === license.periodInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user