mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat: remove path alias.
feat: remove Webpack and depend on nodemon. feat: refactoring expenses. feat: optimize system users with caching. feat: architecture tenant optimize.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Service } from 'typedi';
|
||||
import { Request, Response, Router } from 'express';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
|
||||
@Service()
|
||||
export default class AccountsTypesController extends BaseController{
|
||||
@@ -2,19 +2,19 @@ import { check, query, validationResult, param } from 'express-validator';
|
||||
import express from 'express';
|
||||
import { difference } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import JournalEntry from 'services/Accounting/JournalEntry';
|
||||
import {
|
||||
mapViewRolesToConditionals,
|
||||
mapFilterRolesToDynamicFilter,
|
||||
} from '@/lib/ViewRolesBuilder';
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import {
|
||||
DynamicFilter,
|
||||
DynamicFilterSortBy,
|
||||
DynamicFilterViews,
|
||||
DynamicFilterFilterRoles,
|
||||
} from '@/lib/DynamicFilter';
|
||||
} from 'lib/DynamicFilter';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -1,22 +1,22 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, validationResult, param, query } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import {
|
||||
mapViewRolesToConditionals,
|
||||
mapFilterRolesToDynamicFilter,
|
||||
} from '@/lib/ViewRolesBuilder';
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import {
|
||||
DynamicFilter,
|
||||
DynamicFilterSortBy,
|
||||
DynamicFilterViews,
|
||||
DynamicFilterFilterRoles,
|
||||
} from '@/lib/DynamicFilter';
|
||||
} from 'lib/DynamicFilter';
|
||||
import BaseController from './BaseController';
|
||||
import { IAccountDTO, IAccount } from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import { IAccountDTO, IAccount } from 'interfaces';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import AccountsService from 'services/Accounts/AccountsService';
|
||||
import { Service, Inject } from 'typedi';
|
||||
|
||||
@Service()
|
||||
@@ -265,8 +265,6 @@ export default class AccountsController extends BaseController{
|
||||
await this.accountsService.activateAccount(tenantId, accountId, true);
|
||||
return res.status(200).send({ id: accountId });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
if (error instanceof ServiceError) {
|
||||
this.transformServiceErrorToResponse(res, error);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Router } from 'express'
|
||||
import basicAuth from 'express-basic-auth';
|
||||
import agendash from 'agendash'
|
||||
import { Container } from 'typedi'
|
||||
import config from '@/../config/config'
|
||||
import config from 'config'
|
||||
|
||||
export default class AgendashController {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Request, Response, Router } from 'express';
|
||||
import { check, ValidationChain } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import AuthenticationService from '@/services/Authentication';
|
||||
import { IUserOTD, ISystemUser, IRegisterOTD } from '@/interfaces';
|
||||
import { ServiceError, ServiceErrors } from "@/exceptions";
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import AuthenticationService from 'services/Authentication';
|
||||
import { IUserOTD, ISystemUser, IRegisterOTD } from 'interfaces';
|
||||
import { ServiceError, ServiceErrors } from "exceptions";
|
||||
|
||||
@Service()
|
||||
export default class AuthenticationController extends BaseController{
|
||||
@@ -22,25 +22,25 @@ export default class AuthenticationController extends BaseController{
|
||||
router.post(
|
||||
'/login',
|
||||
this.loginSchema,
|
||||
validateMiddleware,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.login.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/register',
|
||||
this.registerSchema,
|
||||
validateMiddleware,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.register.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/send_reset_password',
|
||||
this.sendResetPasswordSchema,
|
||||
validateMiddleware,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendResetPassword.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/reset/:token',
|
||||
this.resetPasswordSchema,
|
||||
validateMiddleware,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.resetPassword.bind(this))
|
||||
);
|
||||
return router;
|
||||
@@ -64,7 +64,7 @@ export default class AuthenticationController extends BaseController{
|
||||
check('organization_name').exists().trim().escape(),
|
||||
check('first_name').exists().trim().escape(),
|
||||
check('last_name').exists().trim().escape(),
|
||||
check('email').exists().trim().escape(),
|
||||
check('email').exists().isEmail().trim().escape(),
|
||||
check('phone_number').exists().trim().escape(),
|
||||
check('password').exists().trim().escape(),
|
||||
check('country').exists().trim().escape(),
|
||||
@@ -105,11 +105,11 @@ export default class AuthenticationController extends BaseController{
|
||||
const userDTO: IUserOTD = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const { token, user } = await this.authService.signIn(
|
||||
const { token, user, tenant } = await this.authService.signIn(
|
||||
userDTO.crediential,
|
||||
userDTO.password
|
||||
);
|
||||
return res.status(200).send({ token, user });
|
||||
return res.status(200).send({ token, user, tenant });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (['invalid_details', 'invalid_password'].indexOf(error.errorType) !== -1) {
|
||||
@@ -123,7 +123,7 @@ export default class AuthenticationController extends BaseController{
|
||||
});
|
||||
}
|
||||
}
|
||||
next();
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export default class AuthenticationController extends BaseController{
|
||||
return res.boom.badRequest(null, { errors: errorReasons });
|
||||
}
|
||||
}
|
||||
next();
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export default class AuthenticationController extends BaseController{
|
||||
});
|
||||
}
|
||||
}
|
||||
next();
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ export default class AuthenticationController extends BaseController{
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async resetPassword(req: Request, res: Response) {
|
||||
async resetPassword(req: Request, res: Response, next: Function) {
|
||||
const { token } = req.params;
|
||||
const { password } = req.body;
|
||||
|
||||
@@ -214,7 +214,8 @@ export default class AuthenticationController extends BaseController{
|
||||
errors: [{ type: 'USER_NOT_FOUND', code: 120 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Response, Request } from 'express';
|
||||
import { matchedData, validationResult } from "express-validator";
|
||||
import { mapKeys, camelCase, omit } from "lodash";
|
||||
import { camelCase, omit } from "lodash";
|
||||
import { mapKeysDeep } from 'utils'
|
||||
|
||||
export default class BaseController {
|
||||
|
||||
matchedBodyData(req: Request, options: any) {
|
||||
matchedBodyData(req: Request, options: any = {}) {
|
||||
const data = matchedData(req, {
|
||||
locations: ['body'],
|
||||
includeOptionals: true,
|
||||
...omit(options, ['locations']), // override any propery except locations.
|
||||
});
|
||||
return mapKeys(data, (v, k) => camelCase(k));
|
||||
return mapKeysDeep(data, (v, k) => camelCase(k));
|
||||
}
|
||||
|
||||
validationResult(req: Request, res: Response, next: NextFunction) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { check, param, query } from 'express-validator';
|
||||
import BaseController from "@/http/controllers/BaseController";
|
||||
import BaseController from "api/controllers/BaseController";
|
||||
|
||||
export default class ContactsController extends BaseController {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check } from 'express-validator';
|
||||
import ContactsController from '@/http/controllers/Contacts/Contacts';
|
||||
import CustomersService from '@/services/Contacts/CustomersService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ICustomerNewDTO, ICustomerEditDTO } from '@/interfaces';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import ContactsController from 'api/controllers/Contacts/Contacts';
|
||||
import CustomersService from 'services/Contacts/CustomersService';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { ICustomerNewDTO, ICustomerEditDTO } from 'interfaces';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
@Service()
|
||||
export default class CustomersController extends ContactsController {
|
||||
@@ -95,7 +95,7 @@ export default class CustomersController extends ContactsController {
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.customersService.editCustomer(tenantId, contactId, contactDTO;)
|
||||
await this.customersService.editCustomer(tenantId, contactId, contactDTO);
|
||||
return res.status(200).send({ id: contactId });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check } from 'express-validator';
|
||||
import ContactsController from '@/http/controllers/Contacts/Contacts';
|
||||
import VendorsService from '@/services/Contacts/VendorsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { IVendorNewDTO, IVendorEditDTO } from '@/interfaces';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import ContactsController from 'api/controllers/Contacts/Contacts';
|
||||
import VendorsService from 'services/Contacts/VendorsService';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { IVendorNewDTO, IVendorEditDTO } from 'interfaces';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
@Service()
|
||||
export default class VendorsController extends ContactsController {
|
||||
@@ -94,7 +94,7 @@ export default class VendorsController extends ContactsController {
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.vendorsService.editVendor(tenantId, contactId, contactDTO;)
|
||||
await this.vendorsService.editVendor(tenantId, contactId, contactDTO);
|
||||
return res.status(200).send({ id: contactId });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
@@ -146,11 +146,11 @@ export default class VendorsController extends ContactsController {
|
||||
*/
|
||||
async getVendor(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: contactId } = req.params;
|
||||
const { id: vendorId } = req.params;
|
||||
|
||||
try {
|
||||
const contact = await this.vendorsService.getVendor(tenantId, contactId)
|
||||
return res.status(200).send({ contact });
|
||||
const vendor = await this.vendorsService.getVendor(tenantId, vendorId)
|
||||
return res.status(200).send({ vendor });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from 'express';
|
||||
import { check, param, validationResult } from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
export default {
|
||||
/**
|
||||
265
server/src/api/controllers/Expenses.ts
Normal file
265
server/src/api/controllers/Expenses.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
import { Inject, Service } from "typedi";
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BaseController from "api/controllers/BaseController";
|
||||
import ExpensesService from "services/Expenses/ExpensesService";
|
||||
import { IExpenseDTO } from 'interfaces';
|
||||
import { ServiceError } from "exceptions";
|
||||
|
||||
@Service()
|
||||
export default class ExpensesController extends BaseController {
|
||||
@Inject()
|
||||
expensesService: ExpensesService;
|
||||
|
||||
/**
|
||||
* Express router.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/', [
|
||||
...this.expenseDTOSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newExpense.bind(this))
|
||||
);
|
||||
router.post('/publish', [
|
||||
...this.bulkSelectSchema,
|
||||
],
|
||||
this.bulkPublishExpenses.bind(this)
|
||||
);
|
||||
router.post(
|
||||
'/:id/publish', [
|
||||
...this.expenseParamSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.publishExpense.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/:id', [
|
||||
...this.expenseDTOSchema,
|
||||
...this.expenseParamSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editExpense.bind(this)),
|
||||
);
|
||||
router.delete(
|
||||
'/:id', [
|
||||
...this.expenseParamSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteExpense.bind(this))
|
||||
);
|
||||
router.delete('/', [
|
||||
...this.bulkSelectSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.bulkDeleteExpenses.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expense DTO schema.
|
||||
*/
|
||||
get expenseDTOSchema() {
|
||||
return [
|
||||
check('reference_no').optional().trim().escape().isLength({ max: 255 }),
|
||||
check('payment_date').exists().isISO8601(),
|
||||
check('payment_account_id').exists().isNumeric().toInt(),
|
||||
check('description').optional(),
|
||||
check('currency_code').optional(),
|
||||
check('exchange_rate').optional().isNumeric().toFloat(),
|
||||
check('publish').optional().isBoolean().toBoolean(),
|
||||
check('categories').exists().isArray({ min: 1 }),
|
||||
check('categories.*.index').exists().isNumeric().toInt(),
|
||||
check('categories.*.expense_account_id').exists().isNumeric().toInt(),
|
||||
check('categories.*.amount')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.isDecimal()
|
||||
.isFloat({ max: 9999999999.999 }) // 13, 3
|
||||
.toFloat(),
|
||||
check('categories.*.description').optional().trim().escape().isLength({
|
||||
max: 255,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expense param schema.
|
||||
*/
|
||||
get expenseParamSchema() {
|
||||
return [
|
||||
param('id').exists().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
get bulkSelectSchema() {
|
||||
return [
|
||||
query('ids').isArray({ min: 1 }),
|
||||
query('ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expense on
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const expenseDTO: IExpenseDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const expense = await this.expensesService.newExpense(tenantId, expenseDTO, user);
|
||||
return res.status(200).send({ id: expense.id });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(res, error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits details of the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { id: expenseId } = req.params;
|
||||
const expenseDTO: IExpenseDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
await this.expensesService.editExpense(tenantId, expenseId, expenseDTO, user);
|
||||
return res.status(200).send({ id: expenseId });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(res, error);
|
||||
}
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: expenseId } = req.params;
|
||||
|
||||
try {
|
||||
await this.expensesService.deleteExpense(tenantId, expenseId)
|
||||
return res.status(200).send({ id: expenseId });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(res, error);
|
||||
}
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishs the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async publishExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: expenseId } = req.params;
|
||||
|
||||
try {
|
||||
await this.expensesService.publishExpense(tenantId, expenseId)
|
||||
return res.status(200).send({ });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(req, error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the expenses in bulk.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async bulkDeleteExpenses(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { ids: expensesIds } = req.params;
|
||||
|
||||
try {
|
||||
await this.expensesService.deleteBulkExpenses(tenantId, expensesIds);
|
||||
return res.status(200).send({ ids: expensesIds });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(req, error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async bulkPublishExpenses(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
await this.expensesService.publishBulkExpenses(tenantId,);
|
||||
return res.status(200).send({});
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
this.serviceErrorsTransformer(req, error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform service errors to api response errors.
|
||||
* @param {Response} res
|
||||
* @param {ServiceError} error
|
||||
*/
|
||||
serviceErrorsTransformer(res, error: ServiceError) {
|
||||
if (error.errorType === 'expense_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'EXPENSE_NOT_FOUND' }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'total_amount_equals_zero') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'TOTAL.AMOUNT.EQUALS.ZERO' }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'payment_account_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'some_expenses_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'SOME.EXPENSE.ACCOUNTS.NOT.FOUND', code: 200 }]
|
||||
})
|
||||
}
|
||||
if (error.errorType === 'payment_account_has_invalid_type') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'PAYMENT.ACCOUNT.HAS.INVALID.TYPE' }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'expenses_account_has_invalid_type') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'EXPENSES.ACCOUNT.HAS.INVALID.TYPE' }]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import { check, param, validationResult } from 'express-validator';
|
||||
import ResourceField from '@/models/ResourceField';
|
||||
import Resource from '@/models/Resource';
|
||||
import ResourceField from 'models/ResourceField';
|
||||
import Resource from 'models/Resource';
|
||||
import asyncMiddleware from '../middleware/asyncMiddleware';
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,7 @@
|
||||
import moment from 'moment';
|
||||
import { validationResult } from 'express-validator';
|
||||
import { omit, reverse } from 'lodash';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
|
||||
export default class AgingReport extends BaseController{
|
||||
|
||||
@@ -2,11 +2,11 @@ import express from 'express';
|
||||
import { query, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { pick, omit, sumBy } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import { dateRangeCollection, itemsStartWith, getTotalDeep } from '@/utils';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import { dateRangeCollection, itemsStartWith, getTotalDeep } from 'utils';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import { formatNumberClosure } from './FinancialStatementMixin';
|
||||
import BalanceSheetStructure from '@/data/BalanceSheetStructure';
|
||||
import BalanceSheetStructure from 'data/BalanceSheetStructure';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -2,10 +2,10 @@ import express from 'express';
|
||||
import { query, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { pick, difference } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import { formatNumberClosure } from './FinancialStatementMixin';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import DependencyGraph from '@/lib/DependencyGraph';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import DependencyGraph from 'lib/DependencyGraph';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -2,8 +2,8 @@ import express from 'express';
|
||||
import { query, oneOf, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { groupBy } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import { formatNumberClosure } from './FinancialStatementMixin';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import express from 'express';
|
||||
import { query } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import AgingReport from '@/http/controllers/FinancialStatements/AgingReport';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import AgingReport from 'api/controllers/FinancialStatements/AgingReport';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class PayableAgingSummary extends AgingReport {
|
||||
@@ -2,9 +2,9 @@ import express from 'express';
|
||||
import { query, oneOf, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { pick, sumBy } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import { dateRangeCollection } from '@/utils';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import { dateRangeCollection } from 'utils';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import { formatNumberClosure } from './FinancialStatementMixin';
|
||||
|
||||
export default {
|
||||
@@ -1,9 +1,9 @@
|
||||
import express from 'express';
|
||||
import { query, oneOf } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import AgingReport from '@/http/controllers/FinancialStatements/AgingReport';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import AgingReport from 'api/controllers/FinancialStatements/AgingReport';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class ReceivableAgingSummary extends AgingReport {
|
||||
@@ -1,9 +1,9 @@
|
||||
import express from 'express';
|
||||
import { query, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import DependencyGraph from '@/lib/DependencyGraph';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import DependencyGraph from 'lib/DependencyGraph';
|
||||
import { formatNumberClosure }from './FinancialStatementMixin';
|
||||
|
||||
export default {
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
body,
|
||||
param,
|
||||
} from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import InviteUserService from '@/services/InviteUsers';
|
||||
import { ServiceErrors, ServiceError } from '@/exceptions';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import InviteUserService from 'services/InviteUsers';
|
||||
import { ServiceErrors, ServiceError } from 'exceptions';
|
||||
import BaseController from './BaseController';
|
||||
|
||||
@Service()
|
||||
@@ -24,6 +24,7 @@ export default class InviteUsersController extends BaseController {
|
||||
router.post('/send', [
|
||||
body('email').exists().trim().escape(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendInvite.bind(this)),
|
||||
);
|
||||
return router;
|
||||
@@ -38,12 +39,15 @@ export default class InviteUsersController extends BaseController {
|
||||
router.post('/accept/:token', [
|
||||
...this.inviteUserDTO,
|
||||
],
|
||||
asyncMiddleware(this.accept.bind(this)));
|
||||
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.accept.bind(this))
|
||||
);
|
||||
router.get('/invited/:token', [
|
||||
param('token').exists().trim().escape(),
|
||||
],
|
||||
asyncMiddleware(this.invited.bind(this)));
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.invited.bind(this))
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -80,7 +84,6 @@ export default class InviteUsersController extends BaseController {
|
||||
message: 'The invite has been sent to the given email.',
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'email_already_invited') {
|
||||
return res.status(400).send({
|
||||
@@ -6,18 +6,18 @@ import {
|
||||
} from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import { Service } from 'typedi';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import {
|
||||
DynamicFilter,
|
||||
DynamicFilterSortBy,
|
||||
DynamicFilterFilterRoles,
|
||||
} from '@/lib/DynamicFilter';
|
||||
} from 'lib/DynamicFilter';
|
||||
import {
|
||||
mapFilterRolesToDynamicFilter,
|
||||
} from '@/lib/ViewRolesBuilder';
|
||||
import { IItemCategory, IItemCategoryOTD } from '@/interfaces';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import { IItemCategory, IItemCategoryOTD } from 'interfaces';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
|
||||
@Service()
|
||||
export default class ItemsCategoriesController extends BaseController {
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, ValidationChain, matchedData } from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from 'services/DynamicListing/hasDynamicListing';
|
||||
|
||||
@Service()
|
||||
export default class ItemsController {
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import Container from 'typedi';
|
||||
import fs from 'fs';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
const fsPromises = fs.promises;
|
||||
|
||||
114
server/src/api/controllers/Organization.ts
Normal file
114
server/src/api/controllers/Organization.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import asyncMiddleware from "api/middleware/asyncMiddleware";
|
||||
import JWTAuth from 'api/middleware/jwtAuth';
|
||||
import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
|
||||
import SubscriptionMiddleware from 'api/middleware/SubscriptionMiddleware';
|
||||
import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
|
||||
import OrganizationService from 'services/Organization';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
|
||||
@Service()
|
||||
export default class OrganizationController extends BaseController{
|
||||
@Inject()
|
||||
organizationService: OrganizationService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
// Should before build tenant database the user be authorized and
|
||||
// most important than that, should be subscribed to any plan.
|
||||
router.use(JWTAuth);
|
||||
router.use(AttachCurrentTenantUser);
|
||||
router.use(TenancyMiddleware);
|
||||
router.use(SubscriptionMiddleware('main'));
|
||||
|
||||
router.post(
|
||||
'/build',
|
||||
asyncMiddleware(this.build.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/seed',
|
||||
asyncMiddleware(this.seed.bind(this)),
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds tenant database and migrate database schema.
|
||||
* @param {Request} req - Express request.
|
||||
* @param {Response} res - Express response.
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async build(req: Request, res: Response, next: Function) {
|
||||
const { organizationId } = req.tenant;
|
||||
|
||||
try {
|
||||
await this.organizationService.build(organizationId);
|
||||
|
||||
return res.status(200).send({
|
||||
type: 'success',
|
||||
code: 'ORGANIZATION.DATABASE.INITIALIZED',
|
||||
message: 'The organization database has been initialized.'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'tenant_not_found') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'tenant_already_initialized') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.DATABASE.ALREADY.BUILT', code: 200 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(error);
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds initial data to tenant database.
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
async seed(req: Request, res: Response, next: Function) {
|
||||
const { organizationId } = req.tenant;
|
||||
|
||||
try {
|
||||
await this.organizationService.seed(organizationId);
|
||||
|
||||
return res.status(200).send({
|
||||
type: 'success',
|
||||
code: 'ORGANIZATION.DATABASE.SEED',
|
||||
message: 'The organization database has been seeded.'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'tenant_not_found') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'tenant_seeded') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.DATABASE.ALREADY.SEEDED', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'tenant_db_not_built') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.DATABASE.NOT.BUILT', code: 300 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import MomentFormat from 'lib/MomentFormats';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class Ping {
|
||||
/**
|
||||
@@ -2,16 +2,16 @@ import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, matchedData } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { difference } from 'lodash';
|
||||
import { BillOTD } from '@/interfaces';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import BillsService from '@/services/Purchases/Bills';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/HasDynamicListing';
|
||||
import { BillOTD } from 'interfaces';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BillsService from 'services/Purchases/Bills';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import { dynamicListingErrorsToResponse } from 'services/DynamicListing/HasDynamicListing';
|
||||
|
||||
@Service()
|
||||
export default class BillsController extends BaseController {
|
||||
@@ -3,14 +3,14 @@ import { Router } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check, param, query, ValidationChain, matchedData } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import BillPaymentsService from '@/services/Purchases/BillPayments';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import BillPaymentsService from 'services/Purchases/BillPayments';
|
||||
import AccountsService from 'services/Accounts/AccountsService';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import { dynamicListingErrorsToResponse } from 'services/DynamicListing/hasDynamicListing';
|
||||
|
||||
/**
|
||||
* Bills payments controller.
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import { Container } from 'typedi';
|
||||
import Bills from '@/http/controllers/Purchases/Bills'
|
||||
import BillPayments from '@/http/controllers/Purchases/BillsPayments';
|
||||
import Bills from 'api/controllers/Purchases/Bills'
|
||||
import BillPayments from 'api/controllers/Purchases/BillsPayments';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
param,
|
||||
query,
|
||||
} from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -2,17 +2,16 @@ import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, ValidationChain, matchedData } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IPaymentReceive, IPaymentReceiveOTD } from '@/interfaces';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import PaymentReceiveService from '@/services/Sales/PaymentsReceives';
|
||||
import CustomersService from '@/services/Customers/CustomersService';
|
||||
import SaleInvoiceService from '@/services/Sales/SalesInvoices';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
import { IPaymentReceive, IPaymentReceiveOTD } from 'interfaces';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
||||
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
||||
import AccountsService from 'services/Accounts/AccountsService';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from 'services/DynamicListing/hasDynamicListing';
|
||||
|
||||
/**
|
||||
* Payments receives controller.
|
||||
@@ -23,9 +22,6 @@ export default class PaymentReceivesController extends BaseController {
|
||||
@Inject()
|
||||
paymentReceiveService: PaymentReceiveService;
|
||||
|
||||
@Inject()
|
||||
customersService: CustomersService;
|
||||
|
||||
@Inject()
|
||||
accountsService: AccountsService;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, matchedData } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ISaleEstimate, ISaleEstimateOTD } from '@/interfaces';
|
||||
import BaseController from '@/http/controllers/BaseController'
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import CustomersService from '@/services/Customers/CustomersService';
|
||||
import SaleEstimateService from '@/services/Sales/SalesEstimate';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import { ISaleEstimate, ISaleEstimateOTD } from 'interfaces';
|
||||
import BaseController from 'api/controllers/BaseController'
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
|
||||
@Service()
|
||||
export default class SalesEstimatesController extends BaseController {
|
||||
@@ -19,9 +18,6 @@ export default class SalesEstimatesController extends BaseController {
|
||||
@Inject()
|
||||
itemsService: ItemsService;
|
||||
|
||||
@Inject()
|
||||
customersService: CustomersService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
@@ -3,14 +3,14 @@ import { check, param, query, matchedData } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import { raw } from 'objection';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import SaleInvoiceService from '@/services/Sales/SalesInvoices';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
import { ISaleInvoiceOTD } from '@/interfaces';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from 'services/DynamicListing/hasDynamicListing';
|
||||
import { ISaleInvoiceOTD } from 'interfaces';
|
||||
|
||||
@Service()
|
||||
export default class SaleInvoicesController {
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, matchedData } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import SaleReceiptService from '@/services/Sales/SalesReceipts';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import AccountsService from 'services/Accounts/AccountsService';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import SaleReceiptService from 'services/Sales/SalesReceipts';
|
||||
import DynamicListingBuilder from 'services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from 'services/DynamicListing/DynamicListing';
|
||||
import {
|
||||
dynamicListingErrorsToResponse
|
||||
} from '@/services/DynamicListing/HasDynamicListing';
|
||||
} from 'services/DynamicListing/HasDynamicListing';
|
||||
|
||||
@Service()
|
||||
export default class SalesReceiptsController {
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { body, query, validationResult } from 'express-validator';
|
||||
import { pick } from 'lodash';
|
||||
import { IOptionDTO, IOptionsDTO } from '@/interfaces';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import { IOptionDTO, IOptionsDTO } from 'interfaces';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
|
||||
export default class SettingsController extends BaseController{
|
||||
/**
|
||||
@@ -2,13 +2,13 @@ import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response } from 'express'
|
||||
import { check, oneOf, ValidationChain } from 'express-validator';
|
||||
import basicAuth from 'express-basic-auth';
|
||||
import config from '@/../config/config';
|
||||
import { License, Plan } from '@/system/models';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import LicenseService from '@/services/Payment/License';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import { ILicensesFilter } from '@/interfaces';
|
||||
import config from 'config';
|
||||
import { License, Plan } from 'system/models';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import LicenseService from 'services/Payment/License';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import { ILicensesFilter } from 'interfaces';
|
||||
|
||||
@Service()
|
||||
export default class LicensesController extends BaseController {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Inject } from 'typedi';
|
||||
import { Plan } from '@/system/models';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import SubscriptionService from '@/services/Subscription/SubscriptionService';
|
||||
import { Plan } from 'system/models';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import SubscriptionService from 'services/Subscription/SubscriptionService';
|
||||
|
||||
export default class PaymentMethodController extends BaseController {
|
||||
@Inject()
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check } from 'express-validator';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import PaymentMethodController from '@/http/controllers/Subscription/PaymentMethod';
|
||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import PaymentMethodController from 'api/controllers/Subscription/PaymentMethod';
|
||||
import {
|
||||
NotAllowedChangeSubscriptionPlan,
|
||||
NoPaymentModelWithPricedPlan,
|
||||
PaymentAmountInvalidWithPlan,
|
||||
PaymentInputInvalid,
|
||||
} from '@/exceptions';
|
||||
import { ILicensePaymentModel } from '@/interfaces';
|
||||
} from 'exceptions';
|
||||
import { ILicensePaymentModel } from 'interfaces';
|
||||
|
||||
@Service()
|
||||
export default class PaymentViaLicenseController extends PaymentMethodController {
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Router } from 'express'
|
||||
import { Container, Service } from 'typedi';
|
||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||
import AttachCurrentTenantUser from '@/http/middleware/AttachCurrentTenantUser';
|
||||
import PaymentViaLicenseController from '@/http/controllers/Subscription/PaymentViaLicense';
|
||||
import JWTAuth from 'api/middleware/jwtAuth';
|
||||
import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
|
||||
import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
|
||||
import PaymentViaLicenseController from 'api/controllers/Subscription/PaymentViaLicense';
|
||||
|
||||
@Service()
|
||||
export default class SubscriptionController {
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
query,
|
||||
param,
|
||||
} from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import UsersService from '@/services/Users/UsersService';
|
||||
import { ServiceError, ServiceErrors } from '@/exceptions';
|
||||
import { ISystemUserDTO } from '@/interfaces';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import UsersService from 'services/Users/UsersService';
|
||||
import { ServiceError, ServiceErrors } from 'exceptions';
|
||||
import { ISystemUserDTO } from 'interfaces';
|
||||
|
||||
@Service()
|
||||
export default class UsersController extends BaseController{
|
||||
@@ -101,8 +101,6 @@ export default class UsersController extends BaseController{
|
||||
await this.usersService.editUser(tenantId, userId, userDTO);
|
||||
return res.status(200).send({ id: userId });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
if (error instanceof ServiceErrors) {
|
||||
const errorReasons = [];
|
||||
|
||||
@@ -116,6 +114,7 @@ export default class UsersController extends BaseController{
|
||||
return res.status(400).send({ errors: errorReasons });
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
oneOf,
|
||||
validationResult,
|
||||
} from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import {
|
||||
validateViewRoles,
|
||||
} from '@/lib/ViewRolesBuilder';
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default {
|
||||
resource: 'items',
|
||||
@@ -2,17 +2,17 @@ import express from 'express';
|
||||
import { check, param, query, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { difference, sumBy, omit } from 'lodash';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||
import { mapViewRolesToConditionals } from '@/lib/ViewRolesBuilder';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import JournalEntry from 'services/Accounting/JournalEntry';
|
||||
import JWTAuth from 'api/middleware/jwtAuth';
|
||||
import { mapViewRolesToConditionals } from 'lib/ViewRolesBuilder';
|
||||
import {
|
||||
DynamicFilter,
|
||||
DynamicFilterSortBy,
|
||||
DynamicFilterViews,
|
||||
DynamicFilterFilterRoles,
|
||||
} from '@/lib/DynamicFilter';
|
||||
} from 'lib/DynamicFilter';
|
||||
|
||||
export default {
|
||||
/**
|
||||
@@ -2,39 +2,39 @@ import { Router } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
// Middlewares
|
||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||
import AttachCurrentTenantUser from '@/http/middleware/AttachCurrentTenantUser';
|
||||
import SubscriptionMiddleware from '@/http/middleware/SubscriptionMiddleware';
|
||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||
import EnsureTenantIsInitialized from '@/http/middleware/EnsureTenantIsInitialized';
|
||||
import SettingsMiddleware from '@/http/middleware/SettingsMiddleware';
|
||||
import I18nMiddleware from '@/http/middleware/I18nMiddleware';
|
||||
import JWTAuth from 'api/middleware/jwtAuth';
|
||||
import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
|
||||
import SubscriptionMiddleware from 'api/middleware/SubscriptionMiddleware';
|
||||
import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
|
||||
import EnsureTenantIsInitialized from 'api/middleware/EnsureTenantIsInitialized';
|
||||
import SettingsMiddleware from 'api/middleware/SettingsMiddleware';
|
||||
import I18nMiddleware from 'api/middleware/I18nMiddleware';
|
||||
|
||||
// Routes
|
||||
import Authentication from '@/http/controllers/Authentication';
|
||||
import InviteUsers from '@/http/controllers/InviteUsers';
|
||||
import Organization from '@/http/controllers/Organization';
|
||||
import Users from '@/http/controllers/Users';
|
||||
import Items from '@/http/controllers/Items';
|
||||
import ItemCategories from '@/http/controllers/ItemCategories';
|
||||
import Accounts from '@/http/controllers/Accounts';
|
||||
import AccountTypes from '@/http/controllers/AccountTypes';
|
||||
import Views from '@/http/controllers/Views';
|
||||
import Accounting from '@/http/controllers/Accounting';
|
||||
import FinancialStatements from '@/http/controllers/FinancialStatements';
|
||||
import Expenses from '@/http/controllers/Expenses';
|
||||
import Settings from '@/http/controllers/Settings';
|
||||
import Currencies from '@/http/controllers/Currencies';
|
||||
import Customers from '@/http/controllers/Contacts/Customers';
|
||||
import Vendors from '@/http/controllers/Contacts/Vendors';
|
||||
import Sales from '@/http/controllers/Sales'
|
||||
import Purchases from '@/http/controllers/Purchases';
|
||||
import Authentication from 'api/controllers/Authentication';
|
||||
import InviteUsers from 'api/controllers/InviteUsers';
|
||||
import Organization from 'api/controllers/Organization';
|
||||
import Users from 'api/controllers/Users';
|
||||
import Items from 'api/controllers/Items';
|
||||
import ItemCategories from 'api/controllers/ItemCategories';
|
||||
import Accounts from 'api/controllers/Accounts';
|
||||
import AccountTypes from 'api/controllers/AccountTypes';
|
||||
import Views from 'api/controllers/Views';
|
||||
import Accounting from 'api/controllers/Accounting';
|
||||
import FinancialStatements from 'api/controllers/FinancialStatements';
|
||||
import Expenses from 'api/controllers/Expenses';
|
||||
import Settings from 'api/controllers/Settings';
|
||||
import Currencies from 'api/controllers/Currencies';
|
||||
import Customers from 'api/controllers/Contacts/Customers';
|
||||
import Vendors from 'api/controllers/Contacts/Vendors';
|
||||
import Sales from 'api/controllers/Sales'
|
||||
import Purchases from 'api/controllers/Purchases';
|
||||
import Resources from './controllers/Resources';
|
||||
import ExchangeRates from '@/http/controllers/ExchangeRates';
|
||||
import Media from '@/http/controllers/Media';
|
||||
import Ping from '@/http/controllers/Ping';
|
||||
import Subscription from '@/http/controllers/Subscription';
|
||||
import Licenses from '@/http/controllers/Subscription/Licenses';
|
||||
import ExchangeRates from 'api/controllers/ExchangeRates';
|
||||
import Media from 'api/controllers/Media';
|
||||
import Ping from 'api/controllers/Ping';
|
||||
import Subscription from 'api/controllers/Subscription';
|
||||
import Licenses from 'api/controllers/Subscription/Licenses';
|
||||
|
||||
export default () => {
|
||||
const app = Router();
|
||||
@@ -67,7 +67,7 @@ export default () => {
|
||||
dashboard.use('/views', Views.router());
|
||||
dashboard.use('/items', Container.get(Items).router());
|
||||
dashboard.use('/item_categories', Container.get(ItemCategories).router());
|
||||
dashboard.use('/expenses', Expenses.router());
|
||||
dashboard.use('/expenses', Container.get(Expenses).router());
|
||||
dashboard.use('/financial_statements', FinancialStatements.router());
|
||||
dashboard.use('/settings', Container.get(Settings).router());
|
||||
dashboard.use('/sales', Sales.router());
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from 'typedi';
|
||||
import { SystemUser } from '@/system/models';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* Attach user to req.currentUser
|
||||
@@ -9,10 +9,11 @@ import { SystemUser } from '@/system/models';
|
||||
*/
|
||||
const attachCurrentUser = async (req: Request, res: Response, next: Function) => {
|
||||
const Logger = Container.get('logger');
|
||||
const { systemUserRepository } = Container.get('repositories');
|
||||
|
||||
try {
|
||||
Logger.info('[attach_user_middleware] finding system user by id.');
|
||||
const user = await SystemUser.query().findById(req.token.id);
|
||||
const user = await systemUserRepository.getById(req.token.id);
|
||||
|
||||
if (!user) {
|
||||
Logger.info('[attach_user_middleware] the system user not found.');
|
||||
27
server/src/api/middleware/EnsureTenantIsInitialized.ts
Normal file
27
server/src/api/middleware/EnsureTenantIsInitialized.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
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.initializedAt) {
|
||||
Logger.info('[ensure_tenant_initialized_middleware] tenant database not initalized.');
|
||||
return res.boom.badRequest(
|
||||
'Tenant database is not migrated with application schema yut.',
|
||||
{ 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();
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
import SettingsStore from '@/services/Settings/SettingsStore';
|
||||
import SettingsStore from 'services/Settings/SettingsStore';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { tenantId } = req.user;
|
||||
const { knex } = req;
|
||||
|
||||
console.log(knex);
|
||||
|
||||
const Logger = Container.get('logger');
|
||||
const tenantContainer = Container.of(`tenant-${tenantId}`);
|
||||
|
||||
@@ -4,15 +4,13 @@ import { Container } from 'typedi';
|
||||
export default (subscriptionSlug = 'main') => async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { tenant, tenantId } = req;
|
||||
const Logger = Container.get('logger');
|
||||
const { subscriptionRepository } = Container.get('repositories');
|
||||
|
||||
if (!tenant) {
|
||||
throw new Error('Should load `TenancyMiddlware` before this middleware.');
|
||||
}
|
||||
Logger.info('[subscription_middleware] trying get tenant main subscription.');
|
||||
const subscription = await tenant
|
||||
.$relatedQuery('subscriptions')
|
||||
.modify('subscriptionBySlug', subscriptionSlug)
|
||||
.first();
|
||||
const subscription = await subscriptionRepository.getBySlugInTenant(subscriptionSlug, tenantId);
|
||||
|
||||
// Validate in case there is no any already subscription.
|
||||
if (!subscription) {
|
||||
36
server/src/api/middleware/TenancyMiddleware.ts
Normal file
36
server/src/api/middleware/TenancyMiddleware.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import tenantDependencyInjection from 'api/middleware/TenantDependencyInjection'
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
const organizationId = req.headers['organization-id'] || req.query.organization;
|
||||
|
||||
const notFoundOrganization = () => {
|
||||
Logger.info('[tenancy_middleware] organization id not found.');
|
||||
return res.boom.unauthorized(
|
||||
'Organization identication not found.',
|
||||
{ errors: [{ type: 'ORGANIZATION.ID.NOT.FOUND', code: 100 }] },
|
||||
);
|
||||
}
|
||||
// In case the given organization not found.
|
||||
if (!organizationId) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
const { tenantRepository } = Container.get('repositories');
|
||||
|
||||
Logger.info('[tenancy_middleware] trying get tenant by org. id from storage.');
|
||||
const tenant = await tenantRepository.getByOrgId(organizationId);
|
||||
|
||||
// When the given organization id not found on the system storage.
|
||||
if (!tenant) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
// When user tenant not match the given organization id.
|
||||
if (tenant.id !== req.user.tenantId) {
|
||||
Logger.info('[tenancy_middleware] authorized user not match org. tenant.');
|
||||
return res.boom.unauthorized();
|
||||
}
|
||||
tenantDependencyInjection(req, tenant);
|
||||
next();
|
||||
}
|
||||
27
server/src/api/middleware/TenantDependencyInjection.ts
Normal file
27
server/src/api/middleware/TenantDependencyInjection.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Container } from 'typedi';
|
||||
import { ITenant } from 'interfaces';
|
||||
import { Request } from 'express';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import TenantsManagerService from 'services/Tenancy/TenantsManager';
|
||||
|
||||
export default (req: Request, tenant: ITenant) => {
|
||||
const { id: tenantId, organizationId } = tenant;
|
||||
const tenantServices = Container.get(TenancyService);
|
||||
const tenantsManager = Container.get(TenantsManagerService);
|
||||
|
||||
// Initialize the knex instance.
|
||||
tenantsManager.setupKnexInstance(tenant);
|
||||
|
||||
const knexInstance = tenantServices.knex(tenantId);
|
||||
const models = tenantServices.models(tenantId);
|
||||
const repositories = tenantServices.repositories(tenantId)
|
||||
const cacheInstance = tenantServices.cache(tenantId);
|
||||
|
||||
req.knex = knexInstance;
|
||||
req.organizationId = organizationId;
|
||||
req.tenant = tenant;
|
||||
req.tenantId = tenant.id;
|
||||
req.models = models;
|
||||
req.repositories = repositories;
|
||||
req.cache = cacheInstance;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from '@/../config/config';
|
||||
import config from 'config';
|
||||
|
||||
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
8
server/src/before.ts
Normal file
8
server/src/before.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import path from 'path';
|
||||
import moment from 'moment';
|
||||
|
||||
global.__root = path.resolve(__dirname);
|
||||
|
||||
moment.prototype.toMySqlDateTime = function () {
|
||||
return this.format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import MetableCollection from '@/lib/Metable/MetableCollection';
|
||||
import ResourceFieldMetadata from '@/models/ResourceFieldMetadata';
|
||||
import MetableCollection from 'lib/Metable/MetableCollection';
|
||||
import ResourceFieldMetadata from 'models/ResourceFieldMetadata';
|
||||
|
||||
export default class ResourceFieldMetadataCollection extends MetableCollection {
|
||||
/**
|
||||
|
||||
100
server/src/config/index.js
Normal file
100
server/src/config/index.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Set the NODE_ENV to 'development' by default
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
const envFound = dotenv.config();
|
||||
if (envFound.error) {
|
||||
// This error should crash whole process
|
||||
throw new Error("⚠️ Couldn't find .env file ⚠️");
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Your favorite port
|
||||
*/
|
||||
port: parseInt(process.env.PORT, 10),
|
||||
|
||||
system: {
|
||||
db_client: 'mysql',
|
||||
db_host: '127.0.0.1',
|
||||
db_user: 'root',
|
||||
db_password: 'root',
|
||||
db_name: 'bigcapital_system',
|
||||
migrations_dir: './src/system/migrations',
|
||||
seeds_dir: './src/system/seeds',
|
||||
},
|
||||
tenant: {
|
||||
db_client: 'mysql',
|
||||
db_name_prefix: 'bigcapital_tenant_',
|
||||
db_host: '127.0.0.1',
|
||||
db_user: 'root',
|
||||
db_password: 'root',
|
||||
charset: 'utf8',
|
||||
migrations_dir: 'src/database/migrations',
|
||||
seeds_dir: 'src/database/seeds/core',
|
||||
seeds_table_name: 'seeds_versioning',
|
||||
},
|
||||
manager: {
|
||||
superUser: 'root',
|
||||
superPassword: 'root',
|
||||
},
|
||||
mail: {
|
||||
host: 'smtp.mailtrap.io',
|
||||
port: 587,
|
||||
secure: false,
|
||||
username: '842f331d3dc005',
|
||||
password: '172f97b34f1a17',
|
||||
},
|
||||
mongoDb: {
|
||||
/**
|
||||
* That long string from mlab
|
||||
*/
|
||||
databaseURL: 'mongodb://localhost/bigcapital',
|
||||
},
|
||||
/**
|
||||
* Agenda.js stuff
|
||||
*/
|
||||
agenda: {
|
||||
dbCollection: process.env.AGENDA_DB_COLLECTION,
|
||||
pooltime: process.env.AGENDA_POOL_TIME,
|
||||
concurrency: parseInt(process.env.AGENDA_CONCURRENCY, 10),
|
||||
},
|
||||
|
||||
/**
|
||||
* Agendash config
|
||||
*/
|
||||
agendash: {
|
||||
user: 'agendash',
|
||||
password: '123456'
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscription config.
|
||||
*/
|
||||
subscription: {
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
},
|
||||
|
||||
SMSGateway: {
|
||||
type: '',
|
||||
endpoint: '',
|
||||
},
|
||||
easySMSGateway: {
|
||||
api_key: 'b0JDZW56RnV6aEthb0RGPXVEcUI'
|
||||
},
|
||||
jwtSecret: 'b0JDZW56RnV6aEthb0RGPXVEcUI',
|
||||
contactUsMail: 'support@bigcapital.ly',
|
||||
baseURL: 'https://bigcapital.ly',
|
||||
|
||||
api: {
|
||||
prefix: '/api'
|
||||
},
|
||||
resetPasswordSeconds: 600,
|
||||
|
||||
licensesAuth: {
|
||||
user: 'admin',
|
||||
password: 'admin',
|
||||
},
|
||||
};
|
||||
58
server/src/config/knexConfig.ts
Normal file
58
server/src/config/knexConfig.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import config from 'config';
|
||||
import { ITenant } from 'interfaces';
|
||||
|
||||
export const tenantKnexConfig = (tenant: ITenant) => {
|
||||
const { organizationId, id } = tenant;
|
||||
|
||||
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,
|
||||
},
|
||||
pool: { min: 0, max: 5 },
|
||||
userParams: {
|
||||
tenantId: id,
|
||||
organizationId
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const systemKnexConfig = {
|
||||
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,
|
||||
},
|
||||
seeds: {
|
||||
directory: config.system.seeds_dir,
|
||||
},
|
||||
pool: { min: 0, max: 7 },
|
||||
};
|
||||
|
||||
export const systemDbManager = {
|
||||
collate: [],
|
||||
superUser: config.manager.superUser,
|
||||
superPassword: config.manager.superPassword,
|
||||
};
|
||||
|
||||
export const tenantSeedConfig = (tenant: ITenant) => {
|
||||
return {
|
||||
directory: config.tenant.seeds_dir,
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import KnexFactory from '@/lib/KnexFactory';
|
||||
import KnexFactory from 'lib/KnexFactory';
|
||||
import faker from 'faker';
|
||||
import { hashPassword } from '@/utils';
|
||||
import { hashPassword } from 'utils';
|
||||
|
||||
|
||||
export default (tenantDb) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import KnexFactory from '@/lib/KnexFactory';
|
||||
import systemDb from '@/database/knex';
|
||||
import KnexFactory from 'lib/KnexFactory';
|
||||
import systemDb from 'database/knex';
|
||||
import faker from 'faker';
|
||||
|
||||
export default () => {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import knexManager from 'knex-db-manager';
|
||||
import knexfile from '@/../config/systemKnexfile';
|
||||
import config from '@/../config/config';
|
||||
|
||||
const knexConfig = knexfile[process.env.NODE_ENV];
|
||||
|
||||
export default () => knexManager.databaseManagerFactory({
|
||||
knex: knexConfig,
|
||||
dbManager: {
|
||||
collate: [],
|
||||
superUser: config.manager.superUser,
|
||||
superPassword: config.manager.superPassword,
|
||||
},
|
||||
});
|
||||
@@ -3,10 +3,6 @@ exports.up = function (knex) {
|
||||
return knex.schema.createTable('resources', (table) => {
|
||||
table.increments();
|
||||
table.string('name');
|
||||
}).then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_resources.js',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -14,11 +14,7 @@ exports.up = function (knex) {
|
||||
table.decimal('amount', 15, 5);
|
||||
table.string('currency_code', 3);
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_accounts.js',
|
||||
});
|
||||
});
|
||||
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('accounts');
|
||||
|
||||
@@ -9,11 +9,7 @@ exports.up = (knex) => {
|
||||
table.string('child_type');
|
||||
table.boolean('balance_sheet');
|
||||
table.boolean('income_sheet');
|
||||
}).raw('ALTER TABLE `ACCOUNT_TYPES` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_account_types.js',
|
||||
});
|
||||
});
|
||||
}).raw('ALTER TABLE `ACCOUNT_TYPES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('account_types');
|
||||
|
||||
@@ -15,11 +15,7 @@ exports.up = function (knex) {
|
||||
table.string('data_resource');
|
||||
table.json('options');
|
||||
table.integer('resource_id').unsigned();
|
||||
}).raw('ALTER TABLE `RESOURCE_FIELDS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_resources_fields.js',
|
||||
});
|
||||
});
|
||||
}).raw('ALTER TABLE `RESOURCE_FIELDS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('resource_fields');
|
||||
|
||||
@@ -5,9 +5,7 @@ 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(() => {
|
||||
|
||||
});
|
||||
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('view_has_columns');
|
||||
|
||||
@@ -8,11 +8,7 @@ exports.up = function (knex) {
|
||||
table.boolean('favourite');
|
||||
table.string('roles_logic_expression');
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000').then(() => {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_views.js',
|
||||
});
|
||||
});
|
||||
}).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('views');
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('contacts', table => {
|
||||
table.increments();
|
||||
|
||||
table.string('contact_service');
|
||||
table.string('contact_type');
|
||||
|
||||
table.decimal('balance', 13, 3).defaultTo(0);
|
||||
table.decimal('opening_balance', 13, 3).defaultTo(0);
|
||||
|
||||
table.string('first_name').nullable();
|
||||
table.string('last_name').nullable();
|
||||
table.string('company_name').nullable();
|
||||
|
||||
table.string('display_name');
|
||||
|
||||
table.string('email').nullable();
|
||||
table.string('work_phone').nullable();
|
||||
table.string('personal_phone').nullable();
|
||||
|
||||
table.string('billing_address_1').nullable();
|
||||
table.string('billing_address_2').nullable();
|
||||
table.string('billing_address_city').nullable();
|
||||
table.string('billing_address_country').nullable();
|
||||
table.string('billing_address_email').nullable();
|
||||
table.string('billing_address_zipcode').nullable();
|
||||
table.string('billing_address_phone').nullable();
|
||||
table.string('billing_address_state').nullable(),
|
||||
|
||||
table.string('shipping_address_1').nullable();
|
||||
table.string('shipping_address_2').nullable();
|
||||
table.string('shipping_address_city').nullable();
|
||||
table.string('shipping_address_country').nullable();
|
||||
table.string('shipping_address_email').nullable();
|
||||
table.string('shipping_address_zipcode').nullable();
|
||||
table.string('shipping_address_phone').nullable();
|
||||
table.string('shipping_address_state').nullable();
|
||||
|
||||
table.text('note');
|
||||
table.boolean('active').defaultTo(true);
|
||||
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.dropTableIfExists('contacts');
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('customers', table => {
|
||||
table.increments();
|
||||
|
||||
table.string('customer_type');
|
||||
table.decimal('balance', 13, 3).defaultTo(0);
|
||||
|
||||
table.string('first_name').nullable();
|
||||
table.string('last_name').nullable();
|
||||
table.string('company_name').nullable();
|
||||
|
||||
table.string('display_name');
|
||||
|
||||
table.string('email').nullable();
|
||||
table.string('work_phone').nullable();
|
||||
table.string('personal_phone').nullable();
|
||||
|
||||
table.string('billing_address_1').nullable();
|
||||
table.string('billing_address_2').nullable();
|
||||
table.string('billing_address_city').nullable();
|
||||
table.string('billing_address_country').nullable();
|
||||
table.string('billing_address_email').nullable();
|
||||
table.string('billing_address_zipcode').nullable();
|
||||
table.string('billing_address_phone').nullable();
|
||||
table.string('billing_address_state').nullable(),
|
||||
|
||||
table.string('shipping_address_1').nullable();
|
||||
table.string('shipping_address_2').nullable();
|
||||
table.string('shipping_address_city').nullable();
|
||||
table.string('shipping_address_country').nullable();
|
||||
table.string('shipping_address_email').nullable();
|
||||
table.string('shipping_address_zipcode').nullable();
|
||||
table.string('shipping_address_phone').nullable();
|
||||
table.string('shipping_address_state').nullable();
|
||||
|
||||
table.text('note');
|
||||
table.boolean('active').defaultTo(true);
|
||||
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.dropTableIfExists('customers');
|
||||
};
|
||||
182
server/src/database/seeds/core/20190423085240_seed_accounts.js
Normal file
182
server/src/database/seeds/core/20190423085240_seed_accounts.js
Normal file
@@ -0,0 +1,182 @@
|
||||
import TenancyService from 'services/Tenancy/TenancyService'
|
||||
import Container from 'typedi';
|
||||
|
||||
exports.up = function (knex) {
|
||||
const tenancyService = Container.get(TenancyService);
|
||||
const i18n = tenancyService.i18n(knex.userParams.tenantId);
|
||||
|
||||
return knex('accounts').then(() => {
|
||||
// Inserts seed entries
|
||||
return knex('accounts').insert([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Petty Cash',
|
||||
slug: 'petty-cash',
|
||||
account_type_id: 2,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Bank',
|
||||
slug: 'bank',
|
||||
account_type_id: 2,
|
||||
parent_account_id: null,
|
||||
code: '2000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Other Income',
|
||||
slug: 'other-income',
|
||||
account_type_id: 7,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Interest Income',
|
||||
slug: 'interest-income',
|
||||
account_type_id: 7,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Opening Balance',
|
||||
slug: 'opening-balance',
|
||||
account_type_id: 5,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Depreciation Expense',
|
||||
slug: 'depreciation-expense',
|
||||
account_type_id: 6,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Interest Expense',
|
||||
slug: 'interest-expense',
|
||||
account_type_id: 6,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Payroll Expenses',
|
||||
slug: 'payroll-expenses',
|
||||
account_type_id: 6,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Other Expenses',
|
||||
slug: 'other-expenses',
|
||||
account_type_id: 6,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Accounts Receivable',
|
||||
slug: 'accounts-receivable',
|
||||
account_type_id: 8,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Accounts Payable',
|
||||
slug: 'accounts-payable',
|
||||
account_type_id: 9,
|
||||
parent_account_id: null,
|
||||
code: '1000',
|
||||
description: '',
|
||||
active: 1,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Cost of Goods Sold (COGS)',
|
||||
slug: 'cost-of-goods-sold',
|
||||
account_type_id: 12,
|
||||
predefined: 1,
|
||||
parent_account_id: null,
|
||||
index: 1,
|
||||
active: 1,
|
||||
description: 1,
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Inventory Asset',
|
||||
slug: 'inventory-asset',
|
||||
account_type_id: 14,
|
||||
predefined: 1,
|
||||
parent_account_id: null,
|
||||
index: 1,
|
||||
active: 1,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Sales of Product Income',
|
||||
slug: 'sales-of-product-income',
|
||||
account_type_id: 7,
|
||||
predefined: 1,
|
||||
parent_account_id: null,
|
||||
index: 1,
|
||||
active: 1,
|
||||
description: '',
|
||||
}
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
exports.up = function (knex) {
|
||||
return knex('account_types').insert([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Fixed Asset',
|
||||
key: 'fixed_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'fixed_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Current Asset',
|
||||
key: 'current_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'current_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Other Asset',
|
||||
key: 'other_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'other_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Long Term Liability',
|
||||
key: 'long_term_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'long_term_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Current Liability',
|
||||
key: 'current_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'current_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Other Liability',
|
||||
key: 'other_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'other_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Equity',
|
||||
key: 'equity',
|
||||
normal: 'credit',
|
||||
root_type: 'equity',
|
||||
child_type: 'equity',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Expense',
|
||||
key: 'expense',
|
||||
normal: 'debit',
|
||||
root_type: 'expense',
|
||||
child_type: 'expense',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Other Expense',
|
||||
key: 'other_expense',
|
||||
normal: 'debit',
|
||||
root_type: 'expense',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Income',
|
||||
key: 'income',
|
||||
normal: 'credit',
|
||||
root_type: 'income',
|
||||
child_type: 'income',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Other Income',
|
||||
key: 'other_income',
|
||||
normal: 'credit',
|
||||
root_type: 'income',
|
||||
child_type: 'other_income',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Cost of Goods Sold (COGS)',
|
||||
key: 'cost_of_goods_sold',
|
||||
normal: 'debit',
|
||||
root_type: 'expenses',
|
||||
child_type: 'expenses',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Accounts Receivable (A/R)',
|
||||
key: 'accounts_receivable',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'current_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Accounts Payable (A/P)',
|
||||
key: 'accounts_payable',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'current_liability',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
|
||||
exports.seed = (knex) => {
|
||||
// Deletes ALL existing entries
|
||||
return knex('account_types').del()
|
||||
.then(() => {
|
||||
// Inserts seed entries
|
||||
return knex('account_types').insert([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Fixed Asset',
|
||||
key: 'fixed_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'fixed_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Current Asset',
|
||||
key: 'current_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'current_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Other Asset',
|
||||
key: 'other_asset',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'other_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Long Term Liability',
|
||||
key: 'long_term_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'long_term_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Current Liability',
|
||||
key: 'current_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'current_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Other Liability',
|
||||
key: 'other_liability',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'other_liability',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Equity',
|
||||
key: 'equity',
|
||||
normal: 'credit',
|
||||
root_type: 'equity',
|
||||
child_type: 'equity',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Expense',
|
||||
key: 'expense',
|
||||
normal: 'debit',
|
||||
root_type: 'expense',
|
||||
child_type: 'expense',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Other Expense',
|
||||
key: 'other_expense',
|
||||
normal: 'debit',
|
||||
root_type: 'expense',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Income',
|
||||
key: 'income',
|
||||
normal: 'credit',
|
||||
root_type: 'income',
|
||||
child_type: 'income',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Other Income',
|
||||
key: 'other_income',
|
||||
normal: 'credit',
|
||||
root_type: 'income',
|
||||
child_type: 'other_income',
|
||||
balance_sheet: false,
|
||||
income_sheet: true,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Cost of Goods Sold (COGS)',
|
||||
key: 'cost_of_goods_sold',
|
||||
normal: 'debit',
|
||||
root_type: 'expenses',
|
||||
child_type: 'expenses',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Accounts Receivable (A/R)',
|
||||
key: 'accounts_receivable',
|
||||
normal: 'debit',
|
||||
root_type: 'asset',
|
||||
child_type: 'current_asset',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Accounts Payable (A/P)',
|
||||
key: 'accounts_payable',
|
||||
normal: 'credit',
|
||||
root_type: 'liability',
|
||||
child_type: 'current_liability',
|
||||
balance_sheet: true,
|
||||
income_sheet: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
exports.seed = (knex) => {
|
||||
console.log(knex.tenantId);
|
||||
|
||||
// Deletes ALL existing entries
|
||||
return knex('accounts').del()
|
||||
.then(() => {
|
||||
|
||||
7
server/src/exceptions/TenantAlreadyInitialized.ts
Normal file
7
server/src/exceptions/TenantAlreadyInitialized.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
export default class TenantAlreadyInitialized {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
9
server/src/exceptions/TenantAlreadySeeded.ts
Normal file
9
server/src/exceptions/TenantAlreadySeeded.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
|
||||
|
||||
export default class TenantAlreadySeeded {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
9
server/src/exceptions/TenantDBAlreadyExists.ts
Normal file
9
server/src/exceptions/TenantDBAlreadyExists.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
|
||||
|
||||
export default class TenantDBAlreadyExists {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
7
server/src/exceptions/TenantDatabaseNotBuilt.ts
Normal file
7
server/src/exceptions/TenantDatabaseNotBuilt.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
export default class TenantDatabaseNotBuilt {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import ServiceErrors from './ServiceErrors';
|
||||
import NoPaymentModelWithPricedPlan from './NoPaymentModelWithPricedPlan';
|
||||
import PaymentInputInvalid from './PaymentInputInvalid';
|
||||
import PaymentAmountInvalidWithPlan from './PaymentAmountInvalidWithPlan';
|
||||
import TenantAlreadyInitialized from './TenantAlreadyInitialized';
|
||||
import TenantAlreadySeeded from './TenantAlreadySeeded';
|
||||
import TenantDBAlreadyExists from './TenantDBAlreadyExists';
|
||||
import TenantDatabaseNotBuilt from './TenantDatabaseNotBuilt';
|
||||
|
||||
export {
|
||||
NotAllowedChangeSubscriptionPlan,
|
||||
@@ -11,5 +15,9 @@ export {
|
||||
PaymentAmountInvalidWithPlan,
|
||||
ServiceError,
|
||||
ServiceErrors,
|
||||
PaymentInputInvalid
|
||||
PaymentInputInvalid,
|
||||
TenantAlreadyInitialized,
|
||||
TenantAlreadySeeded,
|
||||
TenantDBAlreadyExists,
|
||||
TenantDatabaseNotBuilt,
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check } from 'express-validator';
|
||||
import asyncMiddleware from "@/http/middleware/asyncMiddleware";
|
||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||
import SubscriptionMiddleware from '@/http/middleware/SubscriptionMiddleware';
|
||||
import AttachCurrentTenantUser from '@/http/middleware/AttachCurrentTenantUser';
|
||||
import OrganizationService from '@/services/Organization';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
|
||||
@Service()
|
||||
export default class OrganizationController extends BaseController{
|
||||
@Inject()
|
||||
organizationService: OrganizationService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
// Should before build tenant database the user be authorized and
|
||||
// most important than that, should be subscribed to any plan.
|
||||
router.use(JWTAuth);
|
||||
router.use(AttachCurrentTenantUser);
|
||||
router.use(TenancyMiddleware);
|
||||
router.use(SubscriptionMiddleware('main'));
|
||||
|
||||
router.post(
|
||||
'/build', [
|
||||
check('organization_id').exists().trim().escape(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.build.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds tenant database and seed initial data.
|
||||
* @param {Request} req - Express request.
|
||||
* @param {Response} res - Express response.
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async build(req: Request, res: Response, next: Function) {
|
||||
const buildOTD = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.organizationService.build(buildOTD.organizationId);
|
||||
|
||||
return res.status(200).send({
|
||||
type: 'success',
|
||||
code: 'ORGANIZATION.DATABASE.INITIALIZED',
|
||||
message: 'The organization database has been initialized.'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'tenant_not_found') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'tenant_initialized') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.DATABASE.ALREADY.BUILT', code: 200 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Container } from 'typedi';
|
||||
|
||||
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.initialized) {
|
||||
Logger.info('[ensure_tenant_intialized_middleware] tenant database not initalized.');
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'TENANT.DATABASE.NOT.INITALIZED' }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
@@ -1,68 +0,0 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import TenantsManager from '@/system/TenantsManager';
|
||||
import tenantModelsLoader from '@/loaders/tenantModels';
|
||||
import tenantRepositoriesLoader from '@/loaders/tenantRepositories';
|
||||
import Cache from '@/services/Cache';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
const organizationId = req.headers['organization-id'] || req.query.organization;
|
||||
|
||||
const notFoundOrganization = () => {
|
||||
Logger.info('[tenancy_middleware] organization id not found.');
|
||||
return res.boom.unauthorized(
|
||||
'Organization identication not found.',
|
||||
{ errors: [{ type: 'ORGANIZATION.ID.NOT.FOUND', code: 100 }] },
|
||||
);
|
||||
}
|
||||
// In case the given organization not found.
|
||||
if (!organizationId) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
const tenantsManager = Container.get(TenantsManager);
|
||||
|
||||
Logger.info('[tenancy_middleware] trying get tenant by org. id from storage.');
|
||||
const tenant = await tenantsManager.getTenant(organizationId);
|
||||
|
||||
Logger.info('[tenancy_middleware] initializing tenant knex instance.');
|
||||
const tenantKnex = tenantsManager.knexInstance(organizationId);
|
||||
|
||||
// When the given organization id not found on the system storage.
|
||||
if (!tenant) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
// When user tenant not match the given organization id.
|
||||
if (tenant.id !== req.user.tenantId) {
|
||||
Logger.info('[tenancy_middleware] authorized user not match org. tenant.');
|
||||
return res.boom.unauthorized();
|
||||
}
|
||||
const models = tenantModelsLoader(tenantKnex);
|
||||
|
||||
req.knex = tenantKnex;
|
||||
req.organizationId = organizationId;
|
||||
req.tenant = tenant;
|
||||
req.tenantId = tenant.id;
|
||||
req.models = models;
|
||||
|
||||
const tenantContainer = Container.of(`tenant-${tenant.id}`);
|
||||
const cacheInstance = new Cache();
|
||||
|
||||
tenantContainer.set('models', models);
|
||||
tenantContainer.set('knex', tenantKnex);
|
||||
tenantContainer.set('tenant', tenant);
|
||||
tenantContainer.set('cache', cacheInstance);
|
||||
|
||||
const repositories = tenantRepositoriesLoader(tenant.id)
|
||||
tenantContainer.set('repositories', repositories);
|
||||
|
||||
req.repositories = repositories;
|
||||
|
||||
Logger.info('[tenancy_middleware] tenant dependencies injected to container.');
|
||||
|
||||
if (res.locals) {
|
||||
tenantContainer.set('i18n', res.locals);
|
||||
Logger.info('[tenancy_middleware] i18n locals injected.');
|
||||
}
|
||||
next();
|
||||
}
|
||||
55
server/src/interfaces/Expenses.ts
Normal file
55
server/src/interfaces/Expenses.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
export interface IExpense {
|
||||
id: number,
|
||||
totalAmount: number,
|
||||
currencyCode: string,
|
||||
description?: string,
|
||||
paymentAccountId: number,
|
||||
peyeeId?: number,
|
||||
referenceNo?: string,
|
||||
published: boolean,
|
||||
userId: number,
|
||||
paymentDate: Date,
|
||||
|
||||
categories: IExpenseCategory[],
|
||||
}
|
||||
|
||||
export interface IExpenseCategory {
|
||||
expenseAccountId: number,
|
||||
index: number,
|
||||
description: string,
|
||||
expenseId: number,
|
||||
amount: number,
|
||||
}
|
||||
|
||||
export interface IExpenseDTO {
|
||||
currencyCode: string,
|
||||
description?: string,
|
||||
paymentAccountId: number,
|
||||
peyeeId?: number,
|
||||
referenceNo?: string,
|
||||
published: boolean,
|
||||
userId: number,
|
||||
paymentDate: Date,
|
||||
|
||||
categories: IExpenseCategoryDTO[],
|
||||
}
|
||||
|
||||
export interface IExpenseCategoryDTO {
|
||||
expenseAccountId: number,
|
||||
index: number,
|
||||
description?: string,
|
||||
expenseId: number,
|
||||
};
|
||||
|
||||
export interface IExpensesService {
|
||||
newExpense(tenantid: number, expenseDTO: IExpenseDTO): Promise<IExpense>;
|
||||
editExpense(tenantid: number, expenseId: number, expenseDTO: IExpenseDTO): void;
|
||||
|
||||
publishExpense(tenantId: number, expenseId: number): Promise<void>;
|
||||
|
||||
deleteExpense(tenantId: number, expenseId: number): Promise<void>;
|
||||
deleteBulkExpenses(tenantId: number, expensesIds: number[]): Promise<void>;
|
||||
|
||||
publishBulkExpenses(tenantId: number, expensesIds: number[]): Promise<void>;
|
||||
}
|
||||
49
server/src/interfaces/Tenancy.ts
Normal file
49
server/src/interfaces/Tenancy.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export interface ITenant {
|
||||
id: number,
|
||||
organizationId: string,
|
||||
|
||||
initializedAt: Date|null,
|
||||
seededAt: Date|null,
|
||||
createdAt: Date|null,
|
||||
}
|
||||
|
||||
export interface ITenantDBManager {
|
||||
constructor();
|
||||
|
||||
databaseExists(tenant: ITenant): Promise<boolean>;
|
||||
createDatabase(tenant: ITenant): Promise<void>;
|
||||
|
||||
migrate(tenant: ITenant): Promise<void>;
|
||||
seed(tenant: ITenant): Promise<void>;
|
||||
|
||||
setupKnexInstance(tenant: ITenant): Knex;
|
||||
getKnexInstance(tenantId: number): Knex;
|
||||
}
|
||||
|
||||
export interface ITenantManager {
|
||||
tenantDBManager: ITenantDBManager;
|
||||
tenant: ITenant;
|
||||
|
||||
constructor(): void;
|
||||
|
||||
createTenant(): Promise<ITenant>;
|
||||
createDatabase(tenant: ITenant): Promise<void>;
|
||||
hasDatabase(tenant: ITenant): Promise<boolean>;
|
||||
|
||||
dropTenant(tenant: ITenant): Promise<void>;
|
||||
|
||||
migrateTenant(tenant: ITenant): Promise<void>;
|
||||
seedTenant(tenant: ITenant): Promise<void>;
|
||||
|
||||
setupKnexInstance(tenant: ITenant): Knex;
|
||||
getKnexInstance(tenantId: number): Knex;
|
||||
}
|
||||
|
||||
export interface ISystemService {
|
||||
cache();
|
||||
repositories();
|
||||
knex();
|
||||
dbManager();
|
||||
}
|
||||
@@ -1,13 +1,33 @@
|
||||
|
||||
|
||||
export interface ISystemUser {
|
||||
|
||||
id: number,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
active: boolean,
|
||||
password: string,
|
||||
email: string,
|
||||
phoneNumber: string,
|
||||
|
||||
roleId: number,
|
||||
tenantId: number,
|
||||
|
||||
inviteAcceptAt: Date,
|
||||
lastLoginAt: Date,
|
||||
}
|
||||
|
||||
export interface ISystemUserDTO {
|
||||
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
password: string,
|
||||
phoneNumber: string,
|
||||
active: boolean,
|
||||
email: string,
|
||||
}
|
||||
|
||||
export interface IInviteUserInput {
|
||||
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
phoneNumber: string,
|
||||
password: string,
|
||||
}
|
||||
@@ -66,6 +66,19 @@ import {
|
||||
ICustomerNewDTO,
|
||||
ICustomerEditDTO,
|
||||
} from './Contact';
|
||||
import {
|
||||
IExpense,
|
||||
IExpenseCategory,
|
||||
IExpenseDTO,
|
||||
IExpenseCategoryDTO,
|
||||
IExpensesService,
|
||||
} from './Expenses';
|
||||
import {
|
||||
ITenant,
|
||||
ITenantDBManager,
|
||||
ITenantManager,
|
||||
ISystemService,
|
||||
} from './Tenancy';
|
||||
|
||||
export {
|
||||
IAccount,
|
||||
@@ -126,4 +139,15 @@ export {
|
||||
ICustomer,
|
||||
ICustomerNewDTO,
|
||||
ICustomerEditDTO,
|
||||
|
||||
IExpense,
|
||||
IExpenseCategory,
|
||||
IExpenseDTO,
|
||||
IExpenseCategoryDTO,
|
||||
IExpensesService,
|
||||
|
||||
ITenant,
|
||||
ITenantDBManager,
|
||||
ITenantManager,
|
||||
ISystemService,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Container } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
||||
import InventoryService from 'services/Inventory/Inventory';
|
||||
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||
|
||||
export default class ComputeItemCostJob {
|
||||
depends: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from 'typedi';
|
||||
import SubscriptionService from '@/services/Subscription/Subscription';
|
||||
import SubscriptionService from 'services/Subscription/Subscription';
|
||||
|
||||
export default class MailNotificationSubscribeEnd {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from 'typedi';
|
||||
import SubscriptionService from '@/services/Subscription/Subscription';
|
||||
import SubscriptionService from 'services/Subscription/Subscription';
|
||||
|
||||
export default class MailNotificationTrialEnd {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container, Inject } from 'typedi';
|
||||
import AuthenticationService from '@/services/Authentication';
|
||||
import AuthenticationService from 'services/Authentication';
|
||||
|
||||
export default class WelcomeEmailJob {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from 'typedi';
|
||||
import SubscriptionService from '@/services/Subscription/Subscription';
|
||||
import SubscriptionService from 'services/Subscription/Subscription';
|
||||
|
||||
export default class SMSNotificationSubscribeEnd {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from 'typedi';
|
||||
import SubscriptionService from '@/services/Subscription/Subscription';
|
||||
import SubscriptionService from 'services/Subscription/Subscription';
|
||||
|
||||
export default class SMSNotificationTrialEnd {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from 'typedi';
|
||||
import LicenseService from '@/services/Payment/License';
|
||||
import LicenseService from 'services/Payment/License';
|
||||
|
||||
export default class SendLicenseViaEmailJob {
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from 'typedi';
|
||||
import LicenseService from '@/services/Payment/License';
|
||||
import LicenseService from 'services/Payment/License';
|
||||
|
||||
export default class SendLicenseViaPhoneJob {
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container, Inject } from 'typedi';
|
||||
import InviteUserService from '@/services/InviteUsers';
|
||||
import InviteUserService from 'services/InviteUsers';
|
||||
|
||||
export default class UserInviteMailJob {
|
||||
@Inject()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user