mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
Merge remote-tracking branch 'origin/RegisterWizard' into RegisterWizard
This commit is contained in:
@@ -24,7 +24,7 @@ function WizardSetupSteps({
|
|||||||
{registerWizardSteps.map((step, index) => (
|
{registerWizardSteps.map((step, index) => (
|
||||||
<WizardSetupStep
|
<WizardSetupStep
|
||||||
label={step.label}
|
label={step.label}
|
||||||
isActive={(index + 1) <= currentStep}
|
isActive={(index + 1) == currentStep}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -145,32 +145,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register Form
|
// // Register Form
|
||||||
.register-form {
|
// .register-form {
|
||||||
width: 690px;
|
// // width: 690px;
|
||||||
margin: 0px auto;
|
// // margin: 0px auto;
|
||||||
padding: 85px 50px;
|
// // padding: 85px 50px;
|
||||||
|
|
||||||
|
|
||||||
&__agreement-section {
|
// &__agreement-section {
|
||||||
margin-top: -10px;
|
// margin-top: -10px;
|
||||||
|
|
||||||
p {
|
// p {
|
||||||
font-size: 13px;
|
// font-size: 13px;
|
||||||
margin-top: -10px;
|
// margin-top: -10px;
|
||||||
margin-bottom: 24px;
|
// margin-bottom: 24px;
|
||||||
line-height: 1.65;
|
// line-height: 1.65;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
&__submit-button-wrap {
|
// &__submit-button-wrap {
|
||||||
margin: 25px 0px 25px 0px;
|
// margin: 25px 0px 25px 0px;
|
||||||
|
|
||||||
.bp3-button {
|
// .bp3-button {
|
||||||
min-height: 45px;
|
// min-height: 45px;
|
||||||
background-color: #0052cc;
|
// background-color: #0052cc;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
.send-reset-password {
|
.send-reset-password {
|
||||||
.form-group--crediential {
|
.form-group--crediential {
|
||||||
|
|||||||
@@ -1,11 +1,136 @@
|
|||||||
|
|
||||||
|
.register-page {
|
||||||
|
.bp3-input {
|
||||||
|
min-height: 40px;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
}
|
||||||
|
.bp3-form-group {
|
||||||
|
margin-bottom: 23px;
|
||||||
|
|
||||||
|
&.bp3-intent-danger {
|
||||||
|
.bp3-input {
|
||||||
|
border-color: #eea9a9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bp3-form-group.has-password-revealer {
|
||||||
|
.bp3-label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-revealer {
|
||||||
|
.text {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-button.bp3-fill.bp3-intent-primary {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label-section {
|
||||||
|
margin-bottom: 29px;
|
||||||
|
color: #555;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
// font-weight: 500;
|
||||||
|
font-weight: 400;
|
||||||
|
// font-size: 28px;
|
||||||
|
font-size: 22px;
|
||||||
|
// color: #444;
|
||||||
|
color: #555555;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #0040bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
// max-width: 415px;
|
||||||
|
// padding: 15px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer-links {
|
||||||
|
padding: 9px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0052cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(252, 253, 255, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__submit-button-wrap {
|
||||||
|
margin: 0px 0px 24px 0px;
|
||||||
|
|
||||||
|
.bp3-button {
|
||||||
|
background-color: #0052cc;
|
||||||
|
min-height: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-form {
|
||||||
|
width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
// width: 690px;
|
||||||
|
// padding: 85px 60px;
|
||||||
|
// padding: 85px 105px;
|
||||||
|
|
||||||
|
// Register Form
|
||||||
|
.register-form {
|
||||||
|
padding: 85px 105px;
|
||||||
|
|
||||||
|
&__agreement-section {
|
||||||
|
margin-top: -10px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__submit-button-wrap {
|
||||||
|
margin: 25px 0px 25px 0px;
|
||||||
|
|
||||||
|
.bp3-button {
|
||||||
|
min-height: 45px;
|
||||||
|
background-color: #0052cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.setup-page {
|
.setup-page {
|
||||||
|
|
||||||
&__right-section {
|
&__right-section {
|
||||||
padding-left: 25%;
|
padding-left: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__left-section{
|
&__left-section {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: #778cab;
|
background: #778cab;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -59,6 +184,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.setup-page-steps {
|
.setup-page-steps {
|
||||||
|
|
||||||
@@ -122,9 +249,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// @import './billing.scss';
|
|
||||||
|
|
||||||
//Register Subscription form
|
//Register Subscription form
|
||||||
.register-subscription-form {
|
.register-subscription-form {
|
||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ TENANT_DB_NAME_PERFIX=bigcapital_tenant_
|
|||||||
TENANT_DB_HOST=127.0.0.1
|
TENANT_DB_HOST=127.0.0.1
|
||||||
TENANT_DB_PASSWORD=root
|
TENANT_DB_PASSWORD=root
|
||||||
TEANNT_DB_USER=root
|
TEANNT_DB_USER=root
|
||||||
TENANT_DB_CHARSET=charset
|
TENANT_DB_CHARSET=utf8
|
||||||
TENANT_MIGRATIONS_DIR=src/database/migrations
|
TENANT_MIGRATIONS_DIR=src/database/migrations
|
||||||
TENANT_SEEDS_DIR=src/database/seeds/core
|
TENANT_SEEDS_DIR=src/database/seeds/core
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,15 @@ export default class AccountsController extends BaseController{
|
|||||||
asyncMiddleware(this.inactivateAccount.bind(this)),
|
asyncMiddleware(this.inactivateAccount.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
);
|
);
|
||||||
|
router.post(
|
||||||
|
'/:id/close', [
|
||||||
|
...this.accountParamSchema,
|
||||||
|
...this.closingAccountSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.closeAccount.bind(this)),
|
||||||
|
this.catchServiceErrors,
|
||||||
|
)
|
||||||
router.post(
|
router.post(
|
||||||
'/:id', [
|
'/:id', [
|
||||||
...this.accountDTOSchema,
|
...this.accountDTOSchema,
|
||||||
@@ -127,18 +136,12 @@ export default class AccountsController extends BaseController{
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Account param schema validation.
|
|
||||||
*/
|
|
||||||
get accountParamSchema() {
|
get accountParamSchema() {
|
||||||
return [
|
return [
|
||||||
param('id').exists().isNumeric().toInt()
|
param('id').exists().isNumeric().toInt()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Accounts list schema validation.
|
|
||||||
*/
|
|
||||||
get accountsListSchema() {
|
get accountsListSchema() {
|
||||||
return [
|
return [
|
||||||
query('custom_view_id').optional().isNumeric().toInt(),
|
query('custom_view_id').optional().isNumeric().toInt(),
|
||||||
@@ -149,9 +152,6 @@ export default class AccountsController extends BaseController{
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
get bulkSelectIdsQuerySchema() {
|
get bulkSelectIdsQuerySchema() {
|
||||||
return [
|
return [
|
||||||
query('ids').isArray({ min: 2 }),
|
query('ids').isArray({ min: 2 }),
|
||||||
@@ -159,6 +159,13 @@ export default class AccountsController extends BaseController{
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get closingAccountSchema() {
|
||||||
|
return [
|
||||||
|
check('to_account_id').exists().isNumeric().toInt(),
|
||||||
|
check('delete_after_closing').exists().isBoolean(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new account.
|
* Creates a new account.
|
||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
@@ -328,8 +335,36 @@ export default class AccountsController extends BaseController{
|
|||||||
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const accounts = await this.accountsService.getAccountsList(tenantId, filter);
|
const { accounts, filterMeta } = await this.accountsService.getAccountsList(tenantId, filter);
|
||||||
return res.status(200).send({ accounts });
|
|
||||||
|
return res.status(200).send({
|
||||||
|
accounts,
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the given account.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param next
|
||||||
|
*/
|
||||||
|
async closeAccount(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: accountId } = req.params;
|
||||||
|
const closeAccountQuery = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.accountsService.closeAccount(
|
||||||
|
tenantId,
|
||||||
|
accountId,
|
||||||
|
closeAccountQuery.toAccountId,
|
||||||
|
closeAccountQuery.deleteAfterClosing
|
||||||
|
);
|
||||||
|
return res.status(200).send({ id: accountId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -358,9 +393,8 @@ export default class AccountsController extends BaseController{
|
|||||||
}
|
}
|
||||||
if (error.errorType === 'account_type_not_found') {
|
if (error.errorType === 'account_type_not_found') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'The given account type not found.', {
|
'The given account type not found.',
|
||||||
errors: [{ type: 'ACCOUNT_TYPE_NOT_FOUND', code: 200 }]
|
{ errors: [{ type: 'ACCOUNT_TYPE_NOT_FOUND', code: 200 }] }
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'account_type_not_allowed_to_changed') {
|
if (error.errorType === 'account_type_not_allowed_to_changed') {
|
||||||
@@ -417,6 +451,12 @@ export default class AccountsController extends BaseController{
|
|||||||
{ errors: [{ type: 'ACCOUNTS_PREDEFINED', code: 1100 }] }
|
{ errors: [{ type: 'ACCOUNTS_PREDEFINED', code: 1100 }] }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'close_account_and_to_account_not_same_type') {
|
||||||
|
return res.boom.badRequest(
|
||||||
|
'The close account has different root type with to account.',
|
||||||
|
{ errors: [{ type: 'CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE', code: 1200 }] },
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next(error)
|
next(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ import { Request, Response, Router } from 'express';
|
|||||||
import { check, ValidationChain } from 'express-validator';
|
import { check, ValidationChain } from 'express-validator';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import AuthenticationService from 'services/Authentication';
|
import AuthenticationService from 'services/Authentication';
|
||||||
import { IUserOTD, ISystemUser, IRegisterOTD } from 'interfaces';
|
import { ILoginDTO, ISystemUser, IRegisterOTD } from 'interfaces';
|
||||||
import { ServiceError, ServiceErrors } from "exceptions";
|
import { ServiceError, ServiceErrors } from "exceptions";
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -61,7 +60,6 @@ export default class AuthenticationController extends BaseController{
|
|||||||
*/
|
*/
|
||||||
get registerSchema(): ValidationChain[] {
|
get registerSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
check('organization_name').exists().trim().escape(),
|
|
||||||
check('first_name').exists().trim().escape(),
|
check('first_name').exists().trim().escape(),
|
||||||
check('last_name').exists().trim().escape(),
|
check('last_name').exists().trim().escape(),
|
||||||
check('email').exists().isEmail().trim().escape(),
|
check('email').exists().isEmail().trim().escape(),
|
||||||
@@ -102,7 +100,7 @@ export default class AuthenticationController extends BaseController{
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async login(req: Request, res: Response, next: Function): Response {
|
async login(req: Request, res: Response, next: Function): Response {
|
||||||
const userDTO: IUserOTD = this.matchedBodyData(req);
|
const userDTO: ILoginDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { token, user, tenant } = await this.authService.signIn(
|
const { token, user, tenant } = await this.authService.signIn(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Response, Request, NextFunction } from 'express';
|
import { Response, Request, NextFunction } from 'express';
|
||||||
import { matchedData, validationResult } from "express-validator";
|
import { matchedData, validationResult } from "express-validator";
|
||||||
import { camelCase, omit } from "lodash";
|
import { camelCase, snakeCase, omit } from "lodash";
|
||||||
import { mapKeysDeep } from 'utils'
|
import { mapKeysDeep } from 'utils'
|
||||||
|
|
||||||
export default class BaseController {
|
export default class BaseController {
|
||||||
@@ -55,4 +55,12 @@ export default class BaseController {
|
|||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the given data to response.
|
||||||
|
* @param {any} data
|
||||||
|
*/
|
||||||
|
transfromToResponse(data: any) {
|
||||||
|
return mapKeysDeep(data, (v, k) => snakeCase(k));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,12 @@ export default class CustomersController extends ContactsController {
|
|||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.deleteBulkCustomers.bind(this))
|
asyncMiddleware(this.deleteBulkCustomers.bind(this))
|
||||||
);
|
);
|
||||||
|
router.get('/', [
|
||||||
|
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.getCustomersList.bind(this))
|
||||||
|
);
|
||||||
router.get('/:id', [
|
router.get('/:id', [
|
||||||
...this.specificContactSchema,
|
...this.specificContactSchema,
|
||||||
],
|
],
|
||||||
@@ -193,4 +199,15 @@ export default class CustomersController extends ContactsController {
|
|||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getCustomersList(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.customersService.getCustomersList(tenantId)
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ export default class CurrenciesController extends BaseController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {NextFunction} next
|
* @param {NextFunction} next
|
||||||
*/
|
*/
|
||||||
handlerServiceError(error, req, res, next) {
|
handlerServiceError(error: Error, req: Request, res: Response, next: NextFunction) {
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
if (error.errorType === 'currency_not_found') {
|
if (error.errorType === 'currency_not_found') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import ExpensesService from "services/Expenses/ExpensesService";
|
|||||||
import { IExpenseDTO } from 'interfaces';
|
import { IExpenseDTO } from 'interfaces';
|
||||||
import { ServiceError } from "exceptions";
|
import { ServiceError } from "exceptions";
|
||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
|
import { takeWhile } from "lodash";
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ExpensesController extends BaseController {
|
export default class ExpensesController extends BaseController {
|
||||||
@@ -30,7 +31,8 @@ export default class ExpensesController extends BaseController {
|
|||||||
asyncMiddleware(this.newExpense.bind(this)),
|
asyncMiddleware(this.newExpense.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
);
|
);
|
||||||
router.post('/publish', [
|
router.post(
|
||||||
|
'/publish', [
|
||||||
...this.bulkSelectSchema,
|
...this.bulkSelectSchema,
|
||||||
],
|
],
|
||||||
this.bulkPublishExpenses.bind(this),
|
this.bulkPublishExpenses.bind(this),
|
||||||
@@ -69,11 +71,22 @@ export default class ExpensesController extends BaseController {
|
|||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/', [
|
||||||
|
...this.expensesListSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
asyncMiddleware(this.getExpensesList.bind(this)),
|
asyncMiddleware(this.getExpensesList.bind(this)),
|
||||||
this.dynamicListService.handlerErrorsToResponse,
|
this.dynamicListService.handlerErrorsToResponse,
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
);
|
);
|
||||||
|
router.get(
|
||||||
|
'/:id', [
|
||||||
|
this.expenseParamSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.getExpense.bind(this)),
|
||||||
|
this.catchServiceErrors,
|
||||||
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +102,7 @@ export default class ExpensesController extends BaseController {
|
|||||||
check('currency_code').optional(),
|
check('currency_code').optional(),
|
||||||
check('exchange_rate').optional().isNumeric().toFloat(),
|
check('exchange_rate').optional().isNumeric().toFloat(),
|
||||||
check('publish').optional().isBoolean().toBoolean(),
|
check('publish').optional().isBoolean().toBoolean(),
|
||||||
|
|
||||||
check('categories').exists().isArray({ min: 1 }),
|
check('categories').exists().isArray({ min: 1 }),
|
||||||
check('categories.*.index').exists().isNumeric().toInt(),
|
check('categories.*.index').exists().isNumeric().toInt(),
|
||||||
check('categories.*.expense_account_id').exists().isNumeric().toInt(),
|
check('categories.*.expense_account_id').exists().isNumeric().toInt(),
|
||||||
@@ -120,6 +134,20 @@ export default class ExpensesController extends BaseController {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get expensesListSchema() {
|
||||||
|
return [
|
||||||
|
query('custom_view_id').optional().isNumeric().toInt(),
|
||||||
|
query('stringified_filter_roles').optional().isJSON(),
|
||||||
|
|
||||||
|
query('column_sort_by').optional(),
|
||||||
|
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||||
|
|
||||||
|
query('page').optional().isNumeric().toInt(),
|
||||||
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new expense on
|
* Creates a new expense on
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
@@ -240,12 +268,41 @@ export default class ExpensesController extends BaseController {
|
|||||||
const filter = {
|
const filter = {
|
||||||
filterRoles: [],
|
filterRoles: [],
|
||||||
sortOrder: 'asc',
|
sortOrder: 'asc',
|
||||||
|
columnSortBy: 'created_at',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 12,
|
||||||
...this.matchedQueryData(req),
|
...this.matchedQueryData(req),
|
||||||
};
|
};
|
||||||
|
if (filter.stringifiedFilterRoles) {
|
||||||
|
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const expenses = await this.expensesService.getExpensesList(tenantId, filter);
|
const { expenses, pagination, filterMeta } = await this.expensesService.getExpensesList(tenantId, filter);
|
||||||
return res.status(200).send({ expenses });
|
|
||||||
|
return res.status(200).send({
|
||||||
|
expenses,
|
||||||
|
pagination: this.transfromToResponse(pagination),
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve expense details.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
async getExpense(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: expenseId } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const expense = await this.expensesService.getExpense(tenantId, expenseId);
|
||||||
|
return res.status(200).send({ expense });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -259,29 +316,34 @@ export default class ExpensesController extends BaseController {
|
|||||||
catchServiceErrors(error: Error, req: Request, res: Response, next: NextFunction) {
|
catchServiceErrors(error: Error, req: Request, res: Response, next: NextFunction) {
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
if (error.errorType === 'expense_not_found') {
|
if (error.errorType === 'expense_not_found') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'EXPENSE_NOT_FOUND', code: 100 }],
|
'Expense not found.',
|
||||||
});
|
{ errors: [{ type: 'EXPENSE_NOT_FOUND', code: 100 }] }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'total_amount_equals_zero') {
|
if (error.errorType === 'total_amount_equals_zero') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'TOTAL.AMOUNT.EQUALS.ZERO', code: 200 }],
|
'Expense total should not equal zero.',
|
||||||
});
|
{ errors: [{ type: 'TOTAL.AMOUNT.EQUALS.ZERO', code: 200 }] },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'payment_account_not_found') {
|
if (error.errorType === 'payment_account_not_found') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 300 }],
|
'Payment account not found.',
|
||||||
});
|
{ errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 300 }] },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'some_expenses_not_found') {
|
if (error.errorType === 'some_expenses_not_found') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'SOME.EXPENSE.ACCOUNTS.NOT.FOUND', code: 400 }]
|
'Some expense accounts not found.',
|
||||||
})
|
{ errors: [{ type: 'SOME.EXPENSE.ACCOUNTS.NOT.FOUND', code: 400 }] },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'payment_account_has_invalid_type') {
|
if (error.errorType === 'payment_account_has_invalid_type') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'PAYMENT.ACCOUNT.HAS.INVALID.TYPE', code: 500 }],
|
'Payment account has invalid type.',
|
||||||
});
|
{ errors: [{ type: 'PAYMENT.ACCOUNT.HAS.INVALID.TYPE', code: 500 }], },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'expenses_account_has_invalid_type') {
|
if (error.errorType === 'expenses_account_has_invalid_type') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
|
|||||||
@@ -7,16 +7,19 @@ import {
|
|||||||
import ItemCategoriesService from 'services/ItemCategories/ItemCategoriesService';
|
import ItemCategoriesService from 'services/ItemCategories/ItemCategoriesService';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import { IItemCategoryOTD } from 'interfaces';
|
import { IItemCategoryOTD } from 'interfaces';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ItemsCategoriesController extends BaseController {
|
export default class ItemsCategoriesController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
itemCategoriesService: ItemCategoriesService;
|
itemCategoriesService: ItemCategoriesService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor method.
|
* Router constructor method.
|
||||||
*/
|
*/
|
||||||
@@ -27,44 +30,45 @@ export default class ItemsCategoriesController extends BaseController {
|
|||||||
...this.categoryValidationSchema,
|
...this.categoryValidationSchema,
|
||||||
...this.specificCategoryValidationSchema,
|
...this.specificCategoryValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.editCategory.bind(this)),
|
asyncMiddleware(this.editCategory.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
);
|
);
|
||||||
router.post('/', [
|
router.post('/', [
|
||||||
...this.categoryValidationSchema,
|
...this.categoryValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.newCategory.bind(this)),
|
asyncMiddleware(this.newCategory.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
);
|
);
|
||||||
router.delete('/', [
|
router.delete('/', [
|
||||||
...this.categoriesBulkValidationSchema,
|
...this.categoriesBulkValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.bulkDeleteCategories.bind(this)),
|
asyncMiddleware(this.bulkDeleteCategories.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
);
|
);
|
||||||
router.delete('/:id', [
|
router.delete('/:id', [
|
||||||
...this.specificCategoryValidationSchema
|
...this.specificCategoryValidationSchema
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.deleteItem.bind(this)),
|
asyncMiddleware(this.deleteItem.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
);
|
);
|
||||||
router.get('/:id', [
|
router.get('/:id', [
|
||||||
...this.specificCategoryValidationSchema,
|
...this.specificCategoryValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getCategory.bind(this)),
|
asyncMiddleware(this.getCategory.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
);
|
);
|
||||||
router.get('/', [
|
router.get('/', [
|
||||||
...this.categoriesListValidationSchema
|
...this.categoriesListValidationSchema
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getList.bind(this)),
|
asyncMiddleware(this.getList.bind(this)),
|
||||||
this.handlerServiceError,
|
this.handlerServiceError,
|
||||||
|
this.dynamicListService.handlerErrorsToResponse,
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
@@ -113,8 +117,9 @@ export default class ItemsCategoriesController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
get categoriesListValidationSchema() {
|
get categoriesListValidationSchema() {
|
||||||
return [
|
return [
|
||||||
query('column_sort_order').optional().trim().escape(),
|
query('column_sort_by').optional().trim().escape(),
|
||||||
query('sort_order').optional().trim().escape().isIn(['desc', 'asc']),
|
query('sort_order').optional().trim().escape().isIn(['desc', 'asc']),
|
||||||
|
|
||||||
query('stringified_filter_roles').optional().isJSON(),
|
query('stringified_filter_roles').optional().isJSON(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -190,13 +195,21 @@ export default class ItemsCategoriesController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
async getList(req: Request, res: Response, next: NextFunction) {
|
async getList(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId, user } = req;
|
const { tenantId, user } = req;
|
||||||
const itemCategoriesFilter = this.matchedQueryData(req);
|
const itemCategoriesFilter = {
|
||||||
|
filterRoles: [],
|
||||||
|
sortOrder: 'asc',
|
||||||
|
columnSortBy: 'created_at',
|
||||||
|
...this.matchedQueryData(req),
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const itemCategories = await this.itemCategoriesService.getItemCategoriesList(
|
const { itemCategories, filterMeta } = await this.itemCategoriesService.getItemCategoriesList(
|
||||||
tenantId, itemCategoriesFilter, user,
|
tenantId, itemCategoriesFilter, user,
|
||||||
);
|
);
|
||||||
return res.status(200).send({ item_categories: itemCategories });
|
return res.status(200).send({
|
||||||
|
item_categories: itemCategories,
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ export default class ItemsController extends BaseController {
|
|||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post('/', [
|
||||||
'/', [
|
|
||||||
...this.validateItemSchema,
|
...this.validateItemSchema,
|
||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
@@ -150,10 +149,12 @@ export default class ItemsController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
get validateListQuerySchema() {
|
get validateListQuerySchema() {
|
||||||
return [
|
return [
|
||||||
query('column_sort_order').optional().trim().escape(),
|
query('column_sort_by').optional().trim().escape(),
|
||||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||||
|
|
||||||
query('page').optional().isNumeric().toInt(),
|
query('page').optional().isNumeric().toInt(),
|
||||||
query('page_size').optional().isNumeric().toInt(),
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
|
||||||
query('custom_view_id').optional().isNumeric().toInt(),
|
query('custom_view_id').optional().isNumeric().toInt(),
|
||||||
query('stringified_filter_roles').optional().isJSON(),
|
query('stringified_filter_roles').optional().isJSON(),
|
||||||
]
|
]
|
||||||
@@ -240,14 +241,22 @@ export default class ItemsController extends BaseController {
|
|||||||
const filter = {
|
const filter = {
|
||||||
filterRoles: [],
|
filterRoles: [],
|
||||||
sortOrder: 'asc',
|
sortOrder: 'asc',
|
||||||
|
columnSortBy: 'created_at',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 12,
|
||||||
...this.matchedQueryData(req),
|
...this.matchedQueryData(req),
|
||||||
};
|
};
|
||||||
if (filter.stringifiedFilterRoles) {
|
if (filter.stringifiedFilterRoles) {
|
||||||
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const items = await this.itemsService.itemsList(tenantId, filter);
|
const { items, pagination, filterMeta } = await this.itemsService.itemsList(tenantId, filter);
|
||||||
return res.status(200).send({ items });
|
|
||||||
|
return res.status(200).send({
|
||||||
|
items,
|
||||||
|
pagination: this.transfromToResponse(pagination),
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -345,6 +354,16 @@ export default class ItemsController extends BaseController {
|
|||||||
errors: [{ type: 'INVENTORY.ACCOUNT.NOT.CURRENT.ASSET', code: 300 }],
|
errors: [{ type: 'INVENTORY.ACCOUNT.NOT.CURRENT.ASSET', code: 300 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'ITEMS_HAVE_ASSOCIATED_TRANSACTIONS') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{ type: 'ITEMS_HAVE_ASSOCIATED_TRANSACTIONS', code: 310 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'ITEM_HAS_ASSOCIATED_TRANSACTINS') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{ type: 'ITEM_HAS_ASSOCIATED_TRANSACTINS', code: 320 }],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,17 +299,22 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
sortOrder: 'asc',
|
sortOrder: 'asc',
|
||||||
columnSortBy: 'created_at',
|
columnSortBy: 'created_at',
|
||||||
filterRoles: [],
|
filterRoles: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 12,
|
||||||
...this.matchedQueryData(req),
|
...this.matchedQueryData(req),
|
||||||
}
|
}
|
||||||
if (filter.stringifiedFilterRoles) {
|
if (filter.stringifiedFilterRoles) {
|
||||||
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const manualJournals = await this.manualJournalsService.getManualJournals(tenantId, filter);
|
const { manualJournals, pagination, filterMeta } = await this.manualJournalsService.getManualJournals(tenantId, filter);
|
||||||
return res.status(200).send({ manualJournals });
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
manual_journals: manualJournals,
|
||||||
|
pagination: this.transfromToResponse(pagination),
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
|
|
||||||
import express from 'express';
|
|
||||||
import {
|
|
||||||
param,
|
|
||||||
query,
|
|
||||||
validationResult,
|
|
||||||
} from 'express-validator';
|
|
||||||
import Container from 'typedi';
|
|
||||||
import fs from 'fs';
|
|
||||||
import { difference } from 'lodash';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
|
||||||
|
|
||||||
const fsPromises = fs.promises;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Router constructor.
|
|
||||||
*/
|
|
||||||
router() {
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.post('/upload',
|
|
||||||
this.upload.validation,
|
|
||||||
asyncMiddleware(this.upload.handler));
|
|
||||||
|
|
||||||
router.delete('/',
|
|
||||||
this.delete.validation,
|
|
||||||
asyncMiddleware(this.delete.handler));
|
|
||||||
|
|
||||||
router.get('/',
|
|
||||||
this.get.validation,
|
|
||||||
asyncMiddleware(this.get.handler));
|
|
||||||
|
|
||||||
return router;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all or the given attachment ids.
|
|
||||||
*/
|
|
||||||
get: {
|
|
||||||
validation: [
|
|
||||||
query('ids'),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const validationErrors = validationResult(req);
|
|
||||||
|
|
||||||
if (!validationErrors.isEmpty()) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
code: 'validation_error', ...validationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { Media } = req.models;
|
|
||||||
const media = await Media.query().onBuild((builder) => {
|
|
||||||
|
|
||||||
if (req.query.ids) {
|
|
||||||
const ids = Array.isArray(req.query.ids) ? req.query.ids : [req.query.ids];
|
|
||||||
builder.whereIn('id', ids);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({ media });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads the given attachment file.
|
|
||||||
*/
|
|
||||||
upload: {
|
|
||||||
validation: [
|
|
||||||
// check('attachment').exists(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const Logger = Container.get('logger');
|
|
||||||
|
|
||||||
if (!req.files.attachment) {
|
|
||||||
return res.status(400).send({
|
|
||||||
errors: [{ type: 'ATTACHMENT.NOT.FOUND', code: 200 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const publicPath = 'storage/app/public/';
|
|
||||||
const attachmentsMimes = ['image/png', 'image/jpeg'];
|
|
||||||
const { attachment } = req.files;
|
|
||||||
const { Media } = req.models;
|
|
||||||
|
|
||||||
const errorReasons = [];
|
|
||||||
|
|
||||||
// Validate the attachment.
|
|
||||||
if (attachment && attachmentsMimes.indexOf(attachment.mimetype) === -1) {
|
|
||||||
errorReasons.push({ type: 'ATTACHMENT.MINETYPE.NOT.SUPPORTED', code: 160 });
|
|
||||||
}
|
|
||||||
// Catch all error reasons to response 400.
|
|
||||||
if (errorReasons.length > 0) {
|
|
||||||
return res.status(400).send({ errors: errorReasons });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await attachment.mv(`${publicPath}${req.organizationId}/${attachment.md5}.png`);
|
|
||||||
Logger.info('[attachment] uploaded successfully');
|
|
||||||
} catch (error) {
|
|
||||||
Logger.info('[attachment] uploading failed.', { error });
|
|
||||||
}
|
|
||||||
|
|
||||||
const media = await Media.query().insert({
|
|
||||||
attachment_file: `${attachment.md5}.png`,
|
|
||||||
});
|
|
||||||
return res.status(200).send({ media });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the given attachment ids from file system and database.
|
|
||||||
*/
|
|
||||||
delete: {
|
|
||||||
validation: [
|
|
||||||
query('ids').exists().isArray(),
|
|
||||||
query('ids.*').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const Logger = Container.get('logger');
|
|
||||||
const validationErrors = validationResult(req);
|
|
||||||
|
|
||||||
if (!validationErrors.isEmpty()) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
code: 'validation_error', ...validationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { Media, MediaLink } = req.models;
|
|
||||||
const ids = Array.isArray(req.query.ids) ? req.query.ids : [req.query.ids];
|
|
||||||
const media = await Media.query().whereIn('id', ids);
|
|
||||||
const mediaIds = media.map((m) => m.id);
|
|
||||||
const notFoundMedia = difference(ids, mediaIds);
|
|
||||||
|
|
||||||
if (notFoundMedia.length) {
|
|
||||||
return res.status(400).send({
|
|
||||||
errors: [{ type: 'MEDIA.IDS.NOT.FOUND', code: 200, ids: notFoundMedia }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const publicPath = 'storage/app/public/';
|
|
||||||
const tenantPath = `${publicPath}${req.organizationId}`;
|
|
||||||
const unlinkOpers = [];
|
|
||||||
|
|
||||||
media.forEach((mediaModel) => {
|
|
||||||
const oper = fsPromises.unlink(`${tenantPath}/${mediaModel.attachmentFile}`);
|
|
||||||
unlinkOpers.push(oper);
|
|
||||||
});
|
|
||||||
await Promise.all(unlinkOpers).then((resolved) => {
|
|
||||||
resolved.forEach(() => {
|
|
||||||
Logger.info('[attachment] file has been deleted.');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((errors) => {
|
|
||||||
errors.forEach((error) => {
|
|
||||||
Logger.info('[attachment] Delete item attachment file delete failed.', { error });
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
await MediaLink.query().whereIn('media_id', mediaIds).delete();
|
|
||||||
await Media.query().whereIn('id', mediaIds).delete();
|
|
||||||
|
|
||||||
return res.status(200).send();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
212
server/src/api/controllers/Media.ts
Normal file
212
server/src/api/controllers/Media.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
|
||||||
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
|
import {
|
||||||
|
param,
|
||||||
|
query,
|
||||||
|
check,
|
||||||
|
} from 'express-validator';
|
||||||
|
import { camelCase, upperFirst } from 'lodash';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { IMediaLinkDTO } from 'interfaces';
|
||||||
|
import fs from 'fs';
|
||||||
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
|
import BaseController from './BaseController';
|
||||||
|
import MediaService from 'services/Media/MediaService';
|
||||||
|
import { ServiceError } from 'exceptions';
|
||||||
|
|
||||||
|
const fsPromises = fs.promises;
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class MediaController extends BaseController {
|
||||||
|
@Inject()
|
||||||
|
mediaService: MediaService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router constructor.
|
||||||
|
*/
|
||||||
|
router() {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.post('/upload', [
|
||||||
|
...this.uploadValidationSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.uploadMedia.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
|
);
|
||||||
|
router.post('/:id/link', [
|
||||||
|
...this.mediaIdParamSchema,
|
||||||
|
...this.linkValidationSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.linkMedia.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
|
);
|
||||||
|
router.delete('/', [
|
||||||
|
...this.deleteValidationSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.deleteMedia.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
|
);
|
||||||
|
router.get('/:id', [
|
||||||
|
...this.mediaIdParamSchema,
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.getMedia.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
|
);
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
get uploadValidationSchema() {
|
||||||
|
return [
|
||||||
|
// check('attachment'),
|
||||||
|
check('model_name').optional().trim().escape(),
|
||||||
|
check('model_id').optional().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get linkValidationSchema() {
|
||||||
|
return [
|
||||||
|
check('model_name').exists().trim().escape(),
|
||||||
|
check('model_id').exists().isNumeric().toInt(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get deleteValidationSchema() {
|
||||||
|
return [
|
||||||
|
query('ids').exists().isArray(),
|
||||||
|
query('ids.*').exists().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get mediaIdParamSchema() {
|
||||||
|
return [
|
||||||
|
param('id').exists().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all or the given attachment ids.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} req -
|
||||||
|
* @param {NextFunction} req -
|
||||||
|
*/
|
||||||
|
async getMedia(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: mediaId } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const media = await this.mediaService.getMedia(tenantId, mediaId);
|
||||||
|
return res.status(200).send({ media });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads media.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} req -
|
||||||
|
* @param {NextFunction} req -
|
||||||
|
*/
|
||||||
|
async uploadMedia(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { attachment } = req.files
|
||||||
|
|
||||||
|
const linkMediaDTO: IMediaLinkDTO = this.matchedBodyData(req);
|
||||||
|
const modelName = linkMediaDTO.modelName
|
||||||
|
? upperFirst(camelCase(linkMediaDTO.modelName)) : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const media = await this.mediaService.upload(tenantId, attachment, modelName, linkMediaDTO.modelId);
|
||||||
|
return res.status(200).send({ media_id: media.id });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given attachment ids from file system and database.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} req -
|
||||||
|
* @param {NextFunction} req -
|
||||||
|
*/
|
||||||
|
async deleteMedia(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { ids: mediaIds } = req.query;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.mediaService.deleteMedia(tenantId, mediaIds);
|
||||||
|
return res.status(200).send({
|
||||||
|
media_ids: mediaIds
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the given media to the specific resource model.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
async linkMedia(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: mediaId } = req.params;
|
||||||
|
const linkMediaDTO: IMediaLinkDTO = this.matchedBodyData(req);
|
||||||
|
const modelName = upperFirst(camelCase(linkMediaDTO.modelName));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.mediaService.linkMedia(tenantId, mediaId, linkMediaDTO.modelId, modelName);
|
||||||
|
return res.status(200).send({ media_id: mediaId });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler service errors.
|
||||||
|
* @param {Error} error
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
handlerServiceErrors(error, req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (error instanceof ServiceError) {
|
||||||
|
if (error.errorType === 'MINETYPE_NOT_SUPPORTED') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MINETYPE_NOT_SUPPORTED', code: 100, }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'MEDIA_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MEDIA_NOT_FOUND', code: 200 }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'MODEL_NAME_HAS_NO_MEDIA') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MODEL_NAME_HAS_NO_MEDIA', code: 300 }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'MODEL_ID_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MODEL_ID_NOT_FOUND', code: 400 }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'MEDIA_IDS_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MEDIA_IDS_NOT_FOUND', code: 500 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'MEDIA_LINK_EXISTS') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'MEDIA_LINK_EXISTS', code: 600 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -3,7 +3,6 @@ import { check, param, query, matchedData } from 'express-validator';
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import { BillOTD } from 'interfaces';
|
import { BillOTD } from 'interfaces';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import BillsService from 'services/Purchases/Bills';
|
import BillsService from 'services/Purchases/Bills';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
@@ -30,7 +29,7 @@ export default class BillsController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
[...this.billValidationSchema],
|
[...this.billValidationSchema],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateVendorExistance.bind(this)),
|
asyncMiddleware(this.validateVendorExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateItemsIds.bind(this)),
|
asyncMiddleware(this.validateItemsIds.bind(this)),
|
||||||
asyncMiddleware(this.validateBillNumberExists.bind(this)),
|
asyncMiddleware(this.validateBillNumberExists.bind(this)),
|
||||||
@@ -40,7 +39,7 @@ export default class BillsController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/:id',
|
'/:id',
|
||||||
[...this.billValidationSchema, ...this.specificBillValidationSchema],
|
[...this.billValidationSchema, ...this.specificBillValidationSchema],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillExistance.bind(this)),
|
asyncMiddleware(this.validateBillExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateVendorExistance.bind(this)),
|
asyncMiddleware(this.validateVendorExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateItemsIds.bind(this)),
|
asyncMiddleware(this.validateItemsIds.bind(this)),
|
||||||
@@ -51,20 +50,20 @@ export default class BillsController extends BaseController {
|
|||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
[...this.specificBillValidationSchema],
|
[...this.specificBillValidationSchema],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillExistance.bind(this)),
|
asyncMiddleware(this.validateBillExistance.bind(this)),
|
||||||
asyncMiddleware(this.getBill.bind(this))
|
asyncMiddleware(this.getBill.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
[...this.billsListingValidationSchema],
|
[...this.billsListingValidationSchema],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.listingBills.bind(this))
|
asyncMiddleware(this.listingBills.bind(this))
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
[...this.specificBillValidationSchema],
|
[...this.specificBillValidationSchema],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillExistance.bind(this)),
|
asyncMiddleware(this.validateBillExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteBill.bind(this))
|
asyncMiddleware(this.deleteBill.bind(this))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default class BillsPayments extends BaseController {
|
|||||||
router.post('/', [
|
router.post('/', [
|
||||||
...this.billPaymentSchemaValidation,
|
...this.billPaymentSchemaValidation,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)),
|
asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)),
|
||||||
asyncMiddleware(this.validatePaymentAccount.bind(this)),
|
asyncMiddleware(this.validatePaymentAccount.bind(this)),
|
||||||
asyncMiddleware(this.validatePaymentNumber.bind(this)),
|
asyncMiddleware(this.validatePaymentNumber.bind(this)),
|
||||||
@@ -42,7 +42,7 @@ export default class BillsPayments extends BaseController {
|
|||||||
...this.billPaymentSchemaValidation,
|
...this.billPaymentSchemaValidation,
|
||||||
...this.specificBillPaymentValidateSchema,
|
...this.specificBillPaymentValidateSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)),
|
asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)),
|
||||||
asyncMiddleware(this.validatePaymentAccount.bind(this)),
|
asyncMiddleware(this.validatePaymentAccount.bind(this)),
|
||||||
asyncMiddleware(this.validatePaymentNumber.bind(this)),
|
asyncMiddleware(this.validatePaymentNumber.bind(this)),
|
||||||
@@ -53,19 +53,19 @@ export default class BillsPayments extends BaseController {
|
|||||||
)
|
)
|
||||||
router.delete('/:id',
|
router.delete('/:id',
|
||||||
this.specificBillPaymentValidateSchema,
|
this.specificBillPaymentValidateSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillPaymentExistance.bind(this)),
|
asyncMiddleware(this.validateBillPaymentExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteBillPayment.bind(this)),
|
asyncMiddleware(this.deleteBillPayment.bind(this)),
|
||||||
);
|
);
|
||||||
router.get('/:id',
|
router.get('/:id',
|
||||||
this.specificBillPaymentValidateSchema,
|
this.specificBillPaymentValidateSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateBillPaymentExistance.bind(this)),
|
asyncMiddleware(this.validateBillPaymentExistance.bind(this)),
|
||||||
asyncMiddleware(this.getBillPayment.bind(this)),
|
asyncMiddleware(this.getBillPayment.bind(this)),
|
||||||
);
|
);
|
||||||
router.get('/',
|
router.get('/',
|
||||||
this.listingValidationSchema,
|
this.listingValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getBillsPayments.bind(this))
|
asyncMiddleware(this.getBillsPayments.bind(this))
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
@@ -381,6 +381,20 @@ export default class BillsPayments extends BaseController {
|
|||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async getBillsPayments(req: Request, res: Response) {
|
async getBillsPayments(req: Request, res: Response) {
|
||||||
|
const { tenantId } = req.params;
|
||||||
|
const billPaymentsFilter = this.matchedQueryData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { billPayments, pagination, filterMeta } = await this.billPaymentService
|
||||||
|
.listBillPayments(tenantId, billPaymentsFilter);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
bill_payments: billPayments,
|
||||||
|
pagination: this.transfromToResponse(pagination),
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import express from 'express';
|
|
||||||
import {
|
|
||||||
param,
|
|
||||||
query,
|
|
||||||
} from 'express-validator';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Router constructor.
|
|
||||||
*/
|
|
||||||
router() {
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.get('/:resource_slug/data',
|
|
||||||
this.resourceData.validation,
|
|
||||||
asyncMiddleware(this.resourceData.handler));
|
|
||||||
|
|
||||||
router.get('/:resource_slug/columns',
|
|
||||||
this.resourceColumns.validation,
|
|
||||||
asyncMiddleware(this.resourceColumns.handler));
|
|
||||||
|
|
||||||
router.get('/:resource_slug/fields',
|
|
||||||
this.resourceFields.validation,
|
|
||||||
asyncMiddleware(this.resourceFields.handler));
|
|
||||||
|
|
||||||
return router;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve resource data of the given resource key/slug.
|
|
||||||
*/
|
|
||||||
resourceData: {
|
|
||||||
validation: [
|
|
||||||
param('resource_slug').trim().escape().exists(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { AccountType } = req.models;
|
|
||||||
const { resource_slug: resourceSlug } = req.params;
|
|
||||||
|
|
||||||
const data = await AccountType.query();
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
data,
|
|
||||||
resource_slug: resourceSlug,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve resource columns of the given resource.
|
|
||||||
*/
|
|
||||||
resourceColumns: {
|
|
||||||
validation: [
|
|
||||||
param('resource_slug').trim().escape().exists(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { resource_slug: resourceSlug } = req.params;
|
|
||||||
const { Resource } = req.models;
|
|
||||||
|
|
||||||
const resource = await Resource.query()
|
|
||||||
.where('name', resourceSlug)
|
|
||||||
.withGraphFetched('fields')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!resource) {
|
|
||||||
return res.status(400).send({
|
|
||||||
errors: [{ type: 'RESOURCE.SLUG.NOT.FOUND', code: 200 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const resourceFields = resource.fields
|
|
||||||
.filter((field) => field.columnable)
|
|
||||||
.map((field) => ({
|
|
||||||
id: field.id,
|
|
||||||
label: field.labelName,
|
|
||||||
key: field.key,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
resource_columns: resourceFields,
|
|
||||||
resource_slug: resourceSlug,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve resource fields of the given resource.
|
|
||||||
*/
|
|
||||||
resourceFields: {
|
|
||||||
validation: [
|
|
||||||
param('resource_slug').trim().escape().exists(),
|
|
||||||
query('predefined').optional().isBoolean().toBoolean(),
|
|
||||||
query('builtin').optional().isBoolean().toBoolean(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { resource_slug: resourceSlug } = req.params;
|
|
||||||
const { Resource } = req.models;
|
|
||||||
|
|
||||||
const resource = await Resource.query()
|
|
||||||
.where('name', resourceSlug)
|
|
||||||
.withGraphFetched('fields')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!resource) {
|
|
||||||
return res.status(400).send({
|
|
||||||
errors: [{ type: 'RESOURCE.SLUG.NOT.FOUND', code: 200 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return res.status(200).send({
|
|
||||||
resource_fields: resource.fields,
|
|
||||||
resource_slug: resourceSlug,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
44
server/src/api/controllers/Resources.ts
Normal file
44
server/src/api/controllers/Resources.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
|
import {
|
||||||
|
param,
|
||||||
|
query,
|
||||||
|
} from 'express-validator';
|
||||||
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
|
import BaseController from './BaseController';
|
||||||
|
import { Service } from 'typedi';
|
||||||
|
import ResourceFieldsKeys from 'data/ResourceFieldsKeys';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class ResourceController extends BaseController{
|
||||||
|
/**
|
||||||
|
* Router constructor.
|
||||||
|
*/
|
||||||
|
router() {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/:resource_model/fields',
|
||||||
|
this.resourceModelParamSchema,
|
||||||
|
asyncMiddleware(this.resourceFields.bind(this))
|
||||||
|
);
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
get resourceModelParamSchema() {
|
||||||
|
return [
|
||||||
|
param('resource_model').exists().trim().escape(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve resource fields of the given resource.
|
||||||
|
*/
|
||||||
|
resourceFields(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { resource_model: resourceModel } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -4,7 +4,6 @@ import { difference } from 'lodash';
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { IPaymentReceive, IPaymentReceiveOTD } from 'interfaces';
|
import { IPaymentReceive, IPaymentReceiveOTD } from 'interfaces';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
||||||
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
||||||
@@ -34,7 +33,7 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.editPaymentReceiveValidation,
|
this.editPaymentReceiveValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
||||||
asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)),
|
asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateCustomerExistance.bind(this)),
|
||||||
@@ -47,7 +46,7 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
this.newPaymentReceiveValidation,
|
this.newPaymentReceiveValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)),
|
asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateDepositAccount.bind(this)),
|
asyncMiddleware(this.validateDepositAccount.bind(this)),
|
||||||
@@ -58,20 +57,20 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.paymentReceiveValidation,
|
this.paymentReceiveValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
||||||
asyncMiddleware(this.getPaymentReceive.bind(this))
|
asyncMiddleware(this.getPaymentReceive.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
this.validatePaymentReceiveList,
|
this.validatePaymentReceiveList,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getPaymentReceiveList.bind(this)),
|
asyncMiddleware(this.getPaymentReceiveList.bind(this)),
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.paymentReceiveValidation,
|
this.paymentReceiveValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)),
|
||||||
asyncMiddleware(this.deletePaymentReceive.bind(this)),
|
asyncMiddleware(this.deletePaymentReceive.bind(this)),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { check, param, query, matchedData } from 'express-validator';
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { ISaleEstimate, ISaleEstimateOTD } from 'interfaces';
|
import { ISaleEstimate, ISaleEstimateOTD } from 'interfaces';
|
||||||
import BaseController from 'api/controllers/BaseController'
|
import BaseController from 'api/controllers/BaseController'
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||||
import ItemsService from 'services/Items/ItemsService';
|
import ItemsService from 'services/Items/ItemsService';
|
||||||
@@ -25,7 +24,7 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
this.estimateValidationSchema,
|
this.estimateValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateEstimateNumberExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateNumberExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateEstimateEntriesItemsExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateEntriesItemsExistance.bind(this)),
|
||||||
@@ -36,7 +35,7 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
...this.validateSpecificEstimateSchema,
|
...this.validateSpecificEstimateSchema,
|
||||||
...this.estimateValidationSchema,
|
...this.estimateValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateEstimateNumberExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateNumberExistance.bind(this)),
|
||||||
@@ -48,21 +47,21 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
'/:id', [
|
'/:id', [
|
||||||
this.validateSpecificEstimateSchema,
|
this.validateSpecificEstimateSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteEstimate.bind(this))
|
asyncMiddleware(this.deleteEstimate.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.validateSpecificEstimateSchema,
|
this.validateSpecificEstimateSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
asyncMiddleware(this.validateEstimateIdExistance.bind(this)),
|
||||||
asyncMiddleware(this.getEstimate.bind(this))
|
asyncMiddleware(this.getEstimate.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
this.validateEstimateListSchema,
|
this.validateEstimateListSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getEstimates.bind(this))
|
asyncMiddleware(this.getEstimates.bind(this))
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
@@ -298,7 +297,21 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async getEstimates(req: Request, res: Response) {
|
async getEstimates(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const estimatesFilter: ISalesEstimatesFilter = this.matchedQueryData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { salesEstimates, pagination, filterMeta } = await this.saleEstimateService
|
||||||
|
.estimatesList(tenantId, estimatesFilter);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
sales_estimates: this.transfromToResponse(salesEstimates),
|
||||||
|
pagination,
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
import { check, param, query, matchedData } from 'express-validator';
|
import { check, param, query, matchedData } from 'express-validator';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import { raw } from 'objection';
|
import { raw } from 'objection';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
import BaseController from '../BaseController';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
import SaleInvoiceService from 'services/Sales/SalesInvoices';
|
||||||
import ItemsService from 'services/Items/ItemsService';
|
import ItemsService from 'services/Items/ItemsService';
|
||||||
import { ISaleInvoiceOTD } from 'interfaces';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
|
import { ISaleInvoiceOTD, ISalesInvoicesFilter } from 'interfaces';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SaleInvoicesController {
|
export default class SaleInvoicesController extends BaseController{
|
||||||
@Inject()
|
@Inject()
|
||||||
itemsService: ItemsService;
|
itemsService: ItemsService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
saleInvoiceService: SaleInvoiceService;
|
saleInvoiceService: SaleInvoiceService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
@@ -26,7 +30,7 @@ export default class SaleInvoicesController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
this.saleInvoiceValidationSchema,
|
this.saleInvoiceValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)),
|
asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)),
|
||||||
asyncMiddleware(this.validateInvoiceItemsIdsExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceItemsIdsExistance.bind(this)),
|
||||||
@@ -39,7 +43,7 @@ export default class SaleInvoicesController {
|
|||||||
...this.saleInvoiceValidationSchema,
|
...this.saleInvoiceValidationSchema,
|
||||||
...this.specificSaleInvoiceValidation,
|
...this.specificSaleInvoiceValidation,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)),
|
asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)),
|
||||||
@@ -52,7 +56,7 @@ export default class SaleInvoicesController {
|
|||||||
router.delete(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.specificSaleInvoiceValidation,
|
this.specificSaleInvoiceValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteSaleInvoice.bind(this))
|
asyncMiddleware(this.deleteSaleInvoice.bind(this))
|
||||||
);
|
);
|
||||||
@@ -64,13 +68,14 @@ export default class SaleInvoicesController {
|
|||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.specificSaleInvoiceValidation,
|
this.specificSaleInvoiceValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
asyncMiddleware(this.validateInvoiceExistance.bind(this)),
|
||||||
asyncMiddleware(this.getSaleInvoice.bind(this))
|
asyncMiddleware(this.getSaleInvoice.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
this.saleInvoiceListValidationSchema,
|
this.saleInvoiceListValidationSchema,
|
||||||
|
this.validationResult,
|
||||||
asyncMiddleware(this.getSalesInvoices.bind(this))
|
asyncMiddleware(this.getSalesInvoices.bind(this))
|
||||||
)
|
)
|
||||||
return router;
|
return router;
|
||||||
@@ -411,7 +416,21 @@ export default class SaleInvoicesController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
async getSalesInvoices(req, res) {
|
public async getSalesInvoices(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req.params;
|
||||||
|
const salesInvoicesFilter: ISalesInvoicesFilter = req.query;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { salesInvoices, filterMeta, pagination } = await this.saleInvoiceService.salesInvoicesList(
|
||||||
|
tenantId, salesInvoicesFilter,
|
||||||
|
);
|
||||||
|
return res.status(200).send({
|
||||||
|
sales_invoices: salesInvoices,
|
||||||
|
pagination: this.transfromToResponse(pagination),
|
||||||
|
filter_meta: this.transfromToResponse(filterMeta),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { check, param, query, matchedData } from 'express-validator';
|
import { check, param, query, matchedData } from 'express-validator';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import AccountsService from 'services/Accounts/AccountsService';
|
import AccountsService from 'services/Accounts/AccountsService';
|
||||||
import ItemsService from 'services/Items/ItemsService';
|
import ItemsService from 'services/Items/ItemsService';
|
||||||
import SaleReceiptService from 'services/Sales/SalesReceipts';
|
import SaleReceiptService from 'services/Sales/SalesReceipts';
|
||||||
|
import BaseController from '../BaseController';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SalesReceiptsController {
|
export default class SalesReceiptsController extends BaseController{
|
||||||
@Inject()
|
@Inject()
|
||||||
saleReceiptService: SaleReceiptService;
|
saleReceiptService: SaleReceiptService;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export default class SalesReceiptsController {
|
|||||||
...this.specificReceiptValidationSchema,
|
...this.specificReceiptValidationSchema,
|
||||||
...this.salesReceiptsValidationSchema,
|
...this.salesReceiptsValidationSchema,
|
||||||
],
|
],
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateSaleReceiptExistance.bind(this)),
|
asyncMiddleware(this.validateSaleReceiptExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)),
|
asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)),
|
||||||
@@ -40,7 +40,7 @@ export default class SalesReceiptsController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
this.salesReceiptsValidationSchema,
|
this.salesReceiptsValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)),
|
asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)),
|
asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateReceiptItemsIdsExistance.bind(this)),
|
asyncMiddleware(this.validateReceiptItemsIdsExistance.bind(this)),
|
||||||
@@ -49,14 +49,14 @@ export default class SalesReceiptsController {
|
|||||||
router.delete(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
this.specificReceiptValidationSchema,
|
this.specificReceiptValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateSaleReceiptExistance.bind(this)),
|
asyncMiddleware(this.validateSaleReceiptExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteSaleReceipt.bind(this))
|
asyncMiddleware(this.deleteSaleReceipt.bind(this))
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
this.listSalesReceiptsValidationSchema,
|
this.listSalesReceiptsValidationSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.listingSalesReceipts.bind(this))
|
asyncMiddleware(this.listingSalesReceipts.bind(this))
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
@@ -274,7 +274,6 @@ export default class SalesReceiptsController {
|
|||||||
|
|
||||||
const { id: saleReceiptId } = req.params;
|
const { id: saleReceiptId } = req.params;
|
||||||
const saleReceipt = { ...req.body };
|
const saleReceipt = { ...req.body };
|
||||||
|
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
// Handle all errors with reasons messages.
|
// Handle all errors with reasons messages.
|
||||||
@@ -296,7 +295,19 @@ export default class SalesReceiptsController {
|
|||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async listingSalesReceipts(req: Request, res: Response) {
|
async getSalesReceipts(req: Request, res: Response) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const filter = {
|
||||||
|
sortOrder: 'asc',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 12,
|
||||||
|
...this.matchedBodyData(req),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { body, query } from 'express-validator';
|
import { body, query } from 'express-validator';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
isDefinedOptionConfigurable,
|
isDefinedOptionConfigurable,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
|
||||||
|
@Service()
|
||||||
export default class SettingsController extends BaseController{
|
export default class SettingsController extends BaseController{
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import config from 'config';
|
|||||||
import { License, Plan } from 'system/models';
|
import { License, Plan } from 'system/models';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
import LicenseService from 'services/Payment/License';
|
import LicenseService from 'services/Payment/License';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import { ILicensesFilter } from 'interfaces';
|
import { ILicensesFilter } from 'interfaces';
|
||||||
|
|
||||||
@@ -31,13 +30,13 @@ export default class LicensesController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/generate',
|
'/generate',
|
||||||
this.generateLicenseSchema,
|
this.generateLicenseSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePlanExistance.bind(this)),
|
asyncMiddleware(this.validatePlanExistance.bind(this)),
|
||||||
asyncMiddleware(this.generateLicense.bind(this)),
|
asyncMiddleware(this.generateLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/disable/:licenseId',
|
'/disable/:licenseId',
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validateLicenseExistance.bind(this)),
|
asyncMiddleware(this.validateLicenseExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateNotDisabledLicense.bind(this)),
|
asyncMiddleware(this.validateNotDisabledLicense.bind(this)),
|
||||||
asyncMiddleware(this.disableLicense.bind(this)),
|
asyncMiddleware(this.disableLicense.bind(this)),
|
||||||
@@ -45,7 +44,7 @@ export default class LicensesController extends BaseController {
|
|||||||
router.post(
|
router.post(
|
||||||
'/send',
|
'/send',
|
||||||
this.sendLicenseSchemaValidation,
|
this.sendLicenseSchemaValidation,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.sendLicense.bind(this)),
|
asyncMiddleware(this.sendLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { check } from 'express-validator';
|
import { check } from 'express-validator';
|
||||||
import validateMiddleware from 'api/middleware/validateMiddleware';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import PaymentMethodController from 'api/controllers/Subscription/PaymentMethod';
|
import PaymentMethodController from 'api/controllers/Subscription/PaymentMethod';
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +25,7 @@ export default class PaymentViaLicenseController extends PaymentMethodController
|
|||||||
router.post(
|
router.post(
|
||||||
'/payment',
|
'/payment',
|
||||||
this.paymentViaLicenseSchema,
|
this.paymentViaLicenseSchema,
|
||||||
validateMiddleware,
|
this.validationResult,
|
||||||
asyncMiddleware(this.validatePlanSlugExistance.bind(this)),
|
asyncMiddleware(this.validatePlanSlugExistance.bind(this)),
|
||||||
asyncMiddleware(this.paymentViaLicense.bind(this)),
|
asyncMiddleware(this.paymentViaLicense.bind(this)),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -222,19 +222,22 @@ export default class UsersController extends BaseController{
|
|||||||
}
|
}
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
if (error.errorType === 'user_not_found') {
|
if (error.errorType === 'user_not_found') {
|
||||||
return res.status(404).send({
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'USER.NOT.FOUND', code: 100 }],
|
'User not found.',
|
||||||
});
|
{ errors: [{ type: 'USER.NOT.FOUND', code: 100 }] }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_already_active') {
|
if (error.errorType === 'user_already_active') {
|
||||||
return res.status(404).send({
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'USER.ALREADY.ACTIVE', code: 200 }],
|
'User is already active.',
|
||||||
});
|
{ errors: [{ type: 'USER.ALREADY.ACTIVE', code: 200 }] },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_already_inactive') {
|
if (error.errorType === 'user_already_inactive') {
|
||||||
return res.status(404).send({
|
return res.boom.badRequest(
|
||||||
errors: [{ type: 'USER.ALREADY.INACTIVE', code: 200 }],
|
'User is already inactive.',
|
||||||
});
|
{ errors: [{ type: 'USER.ALREADY.INACTIVE', code: 200 }] },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_same_the_authorized_user') {
|
if (error.errorType === 'user_same_the_authorized_user') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
|
|||||||
@@ -1,473 +0,0 @@
|
|||||||
import { difference, pick } from 'lodash';
|
|
||||||
import express from 'express';
|
|
||||||
import {
|
|
||||||
check,
|
|
||||||
query,
|
|
||||||
param,
|
|
||||||
oneOf,
|
|
||||||
validationResult,
|
|
||||||
} from 'express-validator';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
|
||||||
import {
|
|
||||||
validateViewRoles,
|
|
||||||
} from 'lib/ViewRolesBuilder';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
resource: 'items',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router constructor.
|
|
||||||
*/
|
|
||||||
router() {
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.get('/',
|
|
||||||
this.listViews.validation,
|
|
||||||
asyncMiddleware(this.listViews.handler));
|
|
||||||
|
|
||||||
router.post('/',
|
|
||||||
this.createView.validation,
|
|
||||||
asyncMiddleware(this.createView.handler));
|
|
||||||
|
|
||||||
router.post('/:view_id',
|
|
||||||
this.editView.validation,
|
|
||||||
asyncMiddleware(this.editView.handler));
|
|
||||||
|
|
||||||
router.delete('/:view_id',
|
|
||||||
this.deleteView.validation,
|
|
||||||
asyncMiddleware(this.deleteView.handler));
|
|
||||||
|
|
||||||
router.get('/:view_id',
|
|
||||||
asyncMiddleware(this.getView.handler));
|
|
||||||
|
|
||||||
router.get('/:view_id/resource',
|
|
||||||
this.getViewResource.validation,
|
|
||||||
asyncMiddleware(this.getViewResource.handler));
|
|
||||||
|
|
||||||
return router;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List all views that associated with the given resource.
|
|
||||||
*/
|
|
||||||
listViews: {
|
|
||||||
validation: [
|
|
||||||
oneOf([
|
|
||||||
query('resource_name').exists().trim().escape(),
|
|
||||||
], [
|
|
||||||
query('resource_id').exists().isNumeric().toInt(),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { Resource, View } = req.models;
|
|
||||||
const filter = { ...req.query };
|
|
||||||
|
|
||||||
const resource = await Resource.query().onBuild((builder) => {
|
|
||||||
if (filter.resource_id) {
|
|
||||||
builder.where('id', filter.resource_id);
|
|
||||||
}
|
|
||||||
if (filter.resource_name) {
|
|
||||||
builder.where('name', filter.resource_name);
|
|
||||||
}
|
|
||||||
builder.first();
|
|
||||||
});
|
|
||||||
|
|
||||||
const views = await View.query().where('resource_id', resource.id);
|
|
||||||
|
|
||||||
return res.status(200).send({ views });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve view details of the given view id.
|
|
||||||
*/
|
|
||||||
getView: {
|
|
||||||
validation: [
|
|
||||||
param('view_id').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { view_id: viewId } = req.params;
|
|
||||||
const { View } = req.models;
|
|
||||||
|
|
||||||
const view = await View.query()
|
|
||||||
.where('id', viewId)
|
|
||||||
.withGraphFetched('resource')
|
|
||||||
.withGraphFetched('columns')
|
|
||||||
.withGraphFetched('roles.field')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return res.boom.notFound(null, {
|
|
||||||
errors: [{ type: 'VIEW_NOT_FOUND', code: 100 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return res.status(200).send({ view: view.toJSON() });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the given view of the resource.
|
|
||||||
*/
|
|
||||||
deleteView: {
|
|
||||||
validation: [
|
|
||||||
param('view_id').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { View } = req.models;
|
|
||||||
const { view_id: viewId } = req.params;
|
|
||||||
const view = await View.query().findById(viewId);
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return res.boom.notFound(null, {
|
|
||||||
errors: [{ type: 'VIEW_NOT_FOUND', code: 100 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (view.predefined) {
|
|
||||||
return res.boom.badRequest(null, {
|
|
||||||
errors: [{ type: 'PREDEFINED_VIEW', code: 200 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await Promise.all([
|
|
||||||
view.$relatedQuery('roles').delete(),
|
|
||||||
view.$relatedQuery('columns').delete(),
|
|
||||||
]);
|
|
||||||
await View.query().where('id', view.id).delete();
|
|
||||||
|
|
||||||
return res.status(200).send({ id: view.id });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new view.
|
|
||||||
*/
|
|
||||||
createView: {
|
|
||||||
validation: [
|
|
||||||
check('resource_name').exists().escape().trim(),
|
|
||||||
check('name').exists().escape().trim(),
|
|
||||||
check('logic_expression').exists().trim().escape(),
|
|
||||||
check('roles').isArray({ min: 1 }),
|
|
||||||
check('roles.*.field_key').exists().escape().trim(),
|
|
||||||
check('roles.*.comparator').exists(),
|
|
||||||
check('roles.*.value').exists(),
|
|
||||||
check('roles.*.index').exists().isNumeric().toInt(),
|
|
||||||
check('columns').exists().isArray({ min: 1 }),
|
|
||||||
check('columns.*.key').exists().escape().trim(),
|
|
||||||
check('columns.*.index').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const validationErrors = validationResult(req);
|
|
||||||
|
|
||||||
if (!validationErrors.isEmpty()) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
code: 'validation_error', ...validationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
Resource,
|
|
||||||
View,
|
|
||||||
ViewColumn,
|
|
||||||
ViewRole,
|
|
||||||
} = req.models;
|
|
||||||
const form = { roles: [], ...req.body };
|
|
||||||
const resource = await Resource.query().where('name', form.resource_name).first();
|
|
||||||
|
|
||||||
if (!resource) {
|
|
||||||
return res.boom.notFound(null, {
|
|
||||||
errors: [{ type: 'RESOURCE_NOT_FOUND', code: 100 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const errorReasons = [];
|
|
||||||
const fieldsSlugs = form.roles.map((role) => role.field_key);
|
|
||||||
|
|
||||||
const resourceFields = await resource.$relatedQuery('fields');
|
|
||||||
const resourceFieldsKeys = resourceFields.map((f) => f.key);
|
|
||||||
const resourceFieldsKeysMap = new Map(resourceFields.map((field) => [field.key, field]));
|
|
||||||
const columnsKeys = form.columns.map((c) => c.key);
|
|
||||||
|
|
||||||
// The difference between the stored resource fields and submit fields keys.
|
|
||||||
const notFoundFields = difference(fieldsSlugs, resourceFieldsKeys);
|
|
||||||
|
|
||||||
if (notFoundFields.length > 0) {
|
|
||||||
errorReasons.push({ type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100, fields: notFoundFields });
|
|
||||||
}
|
|
||||||
// The difference between the stored resource fields and the submit columns keys.
|
|
||||||
const notFoundColumns = difference(columnsKeys, resourceFieldsKeys);
|
|
||||||
|
|
||||||
if (notFoundColumns.length > 0) {
|
|
||||||
errorReasons.push({ type: 'COLUMNS_NOT_EXIST', code: 200, columns: notFoundColumns });
|
|
||||||
}
|
|
||||||
// Validates the view conditional logic expression.
|
|
||||||
if (!validateViewRoles(form.roles, form.logic_expression)) {
|
|
||||||
errorReasons.push({ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 });
|
|
||||||
}
|
|
||||||
if (errorReasons.length > 0) {
|
|
||||||
return res.boom.badRequest(null, { errors: errorReasons });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save view details.
|
|
||||||
const view = await View.query().insert({
|
|
||||||
name: form.name,
|
|
||||||
predefined: false,
|
|
||||||
resource_id: resource.id,
|
|
||||||
roles_logic_expression: form.logic_expression,
|
|
||||||
});
|
|
||||||
// Save view roles async operations.
|
|
||||||
const saveViewRolesOpers = [];
|
|
||||||
|
|
||||||
form.roles.forEach((role) => {
|
|
||||||
const fieldModel = resourceFieldsKeysMap.get(role.field_key);
|
|
||||||
|
|
||||||
const saveViewRoleOper = ViewRole.query().insert({
|
|
||||||
...pick(role, ['comparator', 'value', 'index']),
|
|
||||||
field_id: fieldModel.id,
|
|
||||||
view_id: view.id,
|
|
||||||
});
|
|
||||||
saveViewRolesOpers.push(saveViewRoleOper);
|
|
||||||
});
|
|
||||||
|
|
||||||
form.columns.forEach((column) => {
|
|
||||||
const fieldModel = resourceFieldsKeysMap.get(column.key);
|
|
||||||
|
|
||||||
const saveViewColumnOper = ViewColumn.query().insert({
|
|
||||||
field_id: fieldModel.id,
|
|
||||||
view_id: view.id,
|
|
||||||
index: column.index,
|
|
||||||
});
|
|
||||||
saveViewRolesOpers.push(saveViewColumnOper);
|
|
||||||
});
|
|
||||||
await Promise.all(saveViewRolesOpers);
|
|
||||||
|
|
||||||
return res.status(200).send({ id: view.id });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit the given custom view metadata.
|
|
||||||
*/
|
|
||||||
editView: {
|
|
||||||
validation: [
|
|
||||||
param('view_id').exists().isNumeric().toInt(),
|
|
||||||
check('name').exists().escape().trim(),
|
|
||||||
check('logic_expression').exists().trim().escape(),
|
|
||||||
|
|
||||||
check('columns').exists().isArray({ min: 1 }),
|
|
||||||
|
|
||||||
check('columns.*.id').optional().isNumeric().toInt(),
|
|
||||||
check('columns.*.key').exists().escape().trim(),
|
|
||||||
check('columns.*.index').exists().isNumeric().toInt(),
|
|
||||||
|
|
||||||
check('roles').isArray(),
|
|
||||||
check('roles.*.id').optional().isNumeric().toInt(),
|
|
||||||
check('roles.*.field_key').exists().escape().trim(),
|
|
||||||
check('roles.*.comparator').exists(),
|
|
||||||
check('roles.*.value').exists(),
|
|
||||||
check('roles.*.index').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { view_id: viewId } = req.params;
|
|
||||||
const validationErrors = validationResult(req);
|
|
||||||
|
|
||||||
if (!validationErrors.isEmpty()) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
code: 'validation_error', ...validationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
View, ViewRole, ViewColumn, Resource,
|
|
||||||
} = req.models;
|
|
||||||
const view = await View.query().where('id', viewId)
|
|
||||||
.withGraphFetched('roles.field')
|
|
||||||
.withGraphFetched('columns')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return res.boom.notFound(null, {
|
|
||||||
errors: [{ type: 'ROLE_NOT_FOUND', code: 100 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const form = { ...req.body };
|
|
||||||
const resource = await Resource.query()
|
|
||||||
.where('id', view.resourceId)
|
|
||||||
.withGraphFetched('fields')
|
|
||||||
.withGraphFetched('views')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
const errorReasons = [];
|
|
||||||
const fieldsSlugs = form.roles.map((role) => role.field_key);
|
|
||||||
const resourceFieldsKeys = resource.fields.map((f) => f.key);
|
|
||||||
const resourceFieldsKeysMap = new Map(resource.fields.map((field) => [field.key, field]));
|
|
||||||
const columnsKeys = form.columns.map((c) => c.key);
|
|
||||||
|
|
||||||
// The difference between the stored resource fields and submit fields keys.
|
|
||||||
const notFoundFields = difference(fieldsSlugs, resourceFieldsKeys);
|
|
||||||
|
|
||||||
// Validate not found resource fields keys.
|
|
||||||
if (notFoundFields.length > 0) {
|
|
||||||
errorReasons.push({
|
|
||||||
type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100, fields: notFoundFields,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// The difference between the stored resource fields and the submit columns keys.
|
|
||||||
const notFoundColumns = difference(columnsKeys, resourceFieldsKeys);
|
|
||||||
|
|
||||||
// Validate not found view columns.
|
|
||||||
if (notFoundColumns.length > 0) {
|
|
||||||
errorReasons.push({ type: 'RESOURCE_COLUMNS_NOT_EXIST', code: 200, columns: notFoundColumns });
|
|
||||||
}
|
|
||||||
// Validates the view conditional logic expression.
|
|
||||||
if (!validateViewRoles(form.roles, form.logic_expression)) {
|
|
||||||
errorReasons.push({ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewRolesIds = view.roles.map((r) => r.id);
|
|
||||||
const viewColumnsIds = view.columns.map((c) => c.id);
|
|
||||||
|
|
||||||
const formUpdatedRoles = form.roles.filter((r) => r.id);
|
|
||||||
const formInsertRoles = form.roles.filter((r) => !r.id);
|
|
||||||
|
|
||||||
const formRolesIds = formUpdatedRoles.map((r) => r.id);
|
|
||||||
|
|
||||||
const formUpdatedColumns = form.columns.filter((r) => r.id);
|
|
||||||
const formInsertedColumns = form.columns.filter((r) => !r.id);
|
|
||||||
const formColumnsIds = formUpdatedColumns.map((r) => r.id);
|
|
||||||
|
|
||||||
const rolesIdsShouldDeleted = difference(viewRolesIds, formRolesIds);
|
|
||||||
const columnsIdsShouldDelete = difference(viewColumnsIds, formColumnsIds);
|
|
||||||
|
|
||||||
const notFoundViewRolesIds = difference(formRolesIds, viewRolesIds);
|
|
||||||
const notFoundViewColumnsIds = difference(viewColumnsIds, viewColumnsIds);
|
|
||||||
|
|
||||||
// Validate the not found view roles ids.
|
|
||||||
if (notFoundViewRolesIds.length) {
|
|
||||||
errorReasons.push({ type: 'VIEW.ROLES.IDS.NOT.FOUND', code: 500, ids: notFoundViewRolesIds });
|
|
||||||
}
|
|
||||||
// Validate the not found view columns ids.
|
|
||||||
if (notFoundViewColumnsIds.length) {
|
|
||||||
errorReasons.push({ type: 'VIEW.COLUMNS.IDS.NOT.FOUND', code: 600, ids: notFoundViewColumnsIds });
|
|
||||||
}
|
|
||||||
if (errorReasons.length > 0) {
|
|
||||||
return res.status(400).send({ errors: errorReasons });
|
|
||||||
}
|
|
||||||
const asyncOpers = [];
|
|
||||||
|
|
||||||
// Save view details.
|
|
||||||
await View.query()
|
|
||||||
.where('id', view.id)
|
|
||||||
.patch({
|
|
||||||
name: form.name,
|
|
||||||
roles_logic_expression: form.logic_expression,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update view roles.
|
|
||||||
if (formUpdatedRoles.length > 0) {
|
|
||||||
formUpdatedRoles.forEach((role) => {
|
|
||||||
const fieldModel = resourceFieldsKeysMap.get(role.field_key);
|
|
||||||
const updateOper = ViewRole.query()
|
|
||||||
.where('id', role.id)
|
|
||||||
.update({
|
|
||||||
...pick(role, ['comparator', 'value', 'index']),
|
|
||||||
field_id: fieldModel.id,
|
|
||||||
});
|
|
||||||
asyncOpers.push(updateOper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Insert a new view roles.
|
|
||||||
if (formInsertRoles.length > 0) {
|
|
||||||
formInsertRoles.forEach((role) => {
|
|
||||||
const fieldModel = resourceFieldsKeysMap.get(role.field_key);
|
|
||||||
const insertOper = ViewRole.query()
|
|
||||||
.insert({
|
|
||||||
...pick(role, ['comparator', 'value', 'index']),
|
|
||||||
field_id: fieldModel.id,
|
|
||||||
view_id: view.id,
|
|
||||||
});
|
|
||||||
asyncOpers.push(insertOper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Delete view roles.
|
|
||||||
if (rolesIdsShouldDeleted.length > 0) {
|
|
||||||
const deleteOper = ViewRole.query()
|
|
||||||
.whereIn('id', rolesIdsShouldDeleted)
|
|
||||||
.delete();
|
|
||||||
asyncOpers.push(deleteOper);
|
|
||||||
}
|
|
||||||
// Insert a new view columns to the storage.
|
|
||||||
if (formInsertedColumns.length > 0) {
|
|
||||||
formInsertedColumns.forEach((column) => {
|
|
||||||
const fieldModel = resourceFieldsKeysMap.get(column.key);
|
|
||||||
const insertOper = ViewColumn.query()
|
|
||||||
.insert({
|
|
||||||
field_id: fieldModel.id,
|
|
||||||
index: column.index,
|
|
||||||
view_id: view.id,
|
|
||||||
});
|
|
||||||
asyncOpers.push(insertOper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Update the view columns on the storage.
|
|
||||||
if (formUpdatedColumns.length > 0) {
|
|
||||||
formUpdatedColumns.forEach((column) => {
|
|
||||||
const updateOper = ViewColumn.query()
|
|
||||||
.where('id', column.id)
|
|
||||||
.update({
|
|
||||||
index: column.index,
|
|
||||||
});
|
|
||||||
asyncOpers.push(updateOper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Delete the view columns from the storage.
|
|
||||||
if (columnsIdsShouldDelete.length > 0) {
|
|
||||||
const deleteOper = ViewColumn.query()
|
|
||||||
.whereIn('id', columnsIdsShouldDelete)
|
|
||||||
.delete();
|
|
||||||
asyncOpers.push(deleteOper);
|
|
||||||
}
|
|
||||||
await Promise.all(asyncOpers);
|
|
||||||
|
|
||||||
return res.status(200).send();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve resource columns that associated to the given custom view.
|
|
||||||
*/
|
|
||||||
getViewResource: {
|
|
||||||
validation: [
|
|
||||||
param('view_id').exists().isNumeric().toInt(),
|
|
||||||
],
|
|
||||||
async handler(req, res) {
|
|
||||||
const { view_id: viewId } = req.params;
|
|
||||||
const { View } = req.models;
|
|
||||||
|
|
||||||
const view = await View.query()
|
|
||||||
.where('id', viewId)
|
|
||||||
.withGraphFetched('resource.fields')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return res.boom.notFound(null, {
|
|
||||||
errors: [{ type: 'VIEW.NOT.FOUND', code: 100 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!view.resource) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
errors: [{ type: 'VIEW.HAS.NOT.ASSOCIATED.RESOURCE', code: 200 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const resourceColumns = view.resource.fields
|
|
||||||
.filter((field) => field.columnable)
|
|
||||||
.map((field) => ({
|
|
||||||
id: field.id,
|
|
||||||
label: field.labelName,
|
|
||||||
key: field.key,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
resource_slug: view.resource.name,
|
|
||||||
resource_columns: resourceColumns,
|
|
||||||
resource_fields: view.resource.fields,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,19 +1,10 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Router, Request, NextFunction, Response } from 'express';
|
import { Router, Request, NextFunction, Response } from 'express';
|
||||||
import {
|
import { check, param } from 'express-validator';
|
||||||
check,
|
|
||||||
query,
|
|
||||||
param,
|
|
||||||
oneOf,
|
|
||||||
validationResult,
|
|
||||||
} from 'express-validator';
|
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import {
|
|
||||||
validateViewRoles,
|
|
||||||
} from 'lib/ViewRolesBuilder';
|
|
||||||
import ViewsService from 'services/Views/ViewsService';
|
import ViewsService from 'services/Views/ViewsService';
|
||||||
import BaseController from './BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
import { IViewDTO } from 'interfaces';
|
import { IViewDTO, IViewEditDTO } from 'interfaces';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -27,66 +18,96 @@ export default class ViewsController extends BaseController{
|
|||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get('/', [
|
router.get('/resource/:resource_model', [
|
||||||
...this.viewDTOSchemaValidation,
|
...this.viewsListSchemaValidation,
|
||||||
],
|
],
|
||||||
asyncMiddleware(this.listViews)
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.listResourceViews.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
);
|
);
|
||||||
router.post('/', [
|
router.post('/', [
|
||||||
...this.viewDTOSchemaValidation,
|
...this.viewDTOSchemaValidation,
|
||||||
],
|
],
|
||||||
asyncMiddleware(this.createView)
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.createView.bind(this)),
|
||||||
|
this.handlerServiceErrors
|
||||||
);
|
);
|
||||||
|
router.post('/:id', [
|
||||||
router.post('/:view_id', [
|
...this.viewParamSchemaValidation,
|
||||||
...this.viewDTOSchemaValidation,
|
...this.viewEditDTOSchemaValidation,
|
||||||
],
|
],
|
||||||
asyncMiddleware(this.editView)
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.editView.bind(this)),
|
||||||
|
this.handlerServiceErrors,
|
||||||
);
|
);
|
||||||
|
router.delete('/:id', [
|
||||||
router.delete('/:view_id', [
|
|
||||||
...this.viewParamSchemaValidation
|
...this.viewParamSchemaValidation
|
||||||
],
|
],
|
||||||
asyncMiddleware(this.deleteView));
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.deleteView.bind(this)),
|
||||||
router.get('/:view_id', [
|
this.handlerServiceErrors,
|
||||||
...this.viewParamSchemaValidation
|
|
||||||
]
|
|
||||||
asyncMiddleware(this.getView)
|
|
||||||
);
|
);
|
||||||
|
router.get('/:id', [
|
||||||
router.get('/:view_id/resource', [
|
|
||||||
...this.viewParamSchemaValidation
|
...this.viewParamSchemaValidation
|
||||||
],
|
],
|
||||||
asyncMiddleware(this.getViewResource)
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.getView.bind(this)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New view DTO schema validation.
|
||||||
|
*/
|
||||||
get viewDTOSchemaValidation() {
|
get viewDTOSchemaValidation() {
|
||||||
return [
|
return [
|
||||||
check('resource_name').exists().escape().trim(),
|
check('resource_model').exists().escape().trim(),
|
||||||
check('name').exists().escape().trim(),
|
check('name').exists().escape().trim(),
|
||||||
check('logic_expression').exists().trim().escape(),
|
check('logic_expression').exists().trim().escape(),
|
||||||
|
|
||||||
check('roles').isArray({ min: 1 }),
|
check('roles').isArray({ min: 1 }),
|
||||||
check('roles.*.field_key').exists().escape().trim(),
|
check('roles.*.field_key').exists().escape().trim(),
|
||||||
check('roles.*.comparator').exists(),
|
check('roles.*.comparator').exists(),
|
||||||
check('roles.*.value').exists(),
|
check('roles.*.value').exists(),
|
||||||
check('roles.*.index').exists().isNumeric().toInt(),
|
check('roles.*.index').exists().isNumeric().toInt(),
|
||||||
|
|
||||||
check('columns').exists().isArray({ min: 1 }),
|
check('columns').exists().isArray({ min: 1 }),
|
||||||
check('columns.*.key').exists().escape().trim(),
|
check('columns.*.field_key').exists().escape().trim(),
|
||||||
|
check('columns.*.index').exists().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit view DTO schema validation.
|
||||||
|
*/
|
||||||
|
get viewEditDTOSchemaValidation() {
|
||||||
|
return [
|
||||||
|
check('name').exists().escape().trim(),
|
||||||
|
check('logic_expression').exists().trim().escape(),
|
||||||
|
|
||||||
|
check('roles').isArray({ min: 1 }),
|
||||||
|
check('roles.*.field_key').exists().escape().trim(),
|
||||||
|
check('roles.*.comparator').exists(),
|
||||||
|
check('roles.*.value').exists(),
|
||||||
|
check('roles.*.index').exists().isNumeric().toInt(),
|
||||||
|
|
||||||
|
check('columns').exists().isArray({ min: 1 }),
|
||||||
|
check('columns.*.field_key').exists().escape().trim(),
|
||||||
check('columns.*.index').exists().isNumeric().toInt(),
|
check('columns.*.index').exists().isNumeric().toInt(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
get viewParamSchemaValidation() {
|
get viewParamSchemaValidation() {
|
||||||
return [
|
return [
|
||||||
|
param('id').exists().isNumeric().toInt(),
|
||||||
]
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get viewsListSchemaValidation() {
|
||||||
|
return [
|
||||||
|
param('resource_model').exists().trim().escape(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all views that associated with the given resource.
|
* List all views that associated with the given resource.
|
||||||
@@ -94,12 +115,12 @@ export default class ViewsController extends BaseController{
|
|||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
* @param {NextFunction} next -
|
* @param {NextFunction} next -
|
||||||
*/
|
*/
|
||||||
listViews(req: Request, res: Response, next: NextFunction) {
|
async listResourceViews(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter = req.query;
|
const { resource_model: resourceModel } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const views = this.viewsService.listViews(tenantId, filter);
|
const views = await this.viewsService.listResourceViews(tenantId, resourceModel);
|
||||||
return res.status(200).send({ views });
|
return res.status(200).send({ views });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
@@ -107,16 +128,17 @@ export default class ViewsController extends BaseController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieve view details with assocaited roles and columns.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {NextFunction} next
|
* @param {NextFunction} next
|
||||||
*/
|
*/
|
||||||
getView(req: Request, res: Response, next: NextFunction) {
|
async getView(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
|
const { id: viewId } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const view = this.viewsService.getView(tenantId, viewId);
|
const view = await this.viewsService.getView(tenantId, viewId);
|
||||||
return res.status(200).send({ view });
|
return res.status(200).send({ view });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
@@ -129,13 +151,13 @@ export default class ViewsController extends BaseController{
|
|||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
* @param {NextFunction} next -
|
* @param {NextFunction} next -
|
||||||
*/
|
*/
|
||||||
createView(req: Request, res: Response, next: NextFunction) {
|
async createView(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const viewDTO: IViewDTO = this.matchedBodyData(req);
|
const viewDTO: IViewDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.viewsService.newView(tenantId, viewDTO);
|
const view = await this.viewsService.newView(tenantId, viewDTO);
|
||||||
return res.status(200).send({ id: 1 });
|
return res.status(200).send({ id: view.id });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -147,10 +169,10 @@ export default class ViewsController extends BaseController{
|
|||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
* @param {NextFunction} next -
|
* @param {NextFunction} next -
|
||||||
*/
|
*/
|
||||||
editView(req: Request, res: Response, next: NextFunction) {
|
async editView(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const { id: viewId } = req.params;
|
const { id: viewId } = req.params;
|
||||||
const { body: viewEditDTO } = req;
|
const viewEditDTO: IViewEditDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.viewsService.editView(tenantId, viewId, viewEditDTO);
|
await this.viewsService.editView(tenantId, viewId, viewEditDTO);
|
||||||
@@ -166,7 +188,7 @@ export default class ViewsController extends BaseController{
|
|||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
* @param {NextFunction} next -
|
* @param {NextFunction} next -
|
||||||
*/
|
*/
|
||||||
deleteView(req: Request, res: Response, next: NextFunction) {
|
async deleteView(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const { id: viewId } = req.params;
|
const { id: viewId } = req.params;
|
||||||
|
|
||||||
@@ -187,6 +209,16 @@ export default class ViewsController extends BaseController{
|
|||||||
*/
|
*/
|
||||||
handlerServiceErrors(error: Error, req: Request, res: Response, next: NextFunction) {
|
handlerServiceErrors(error: Error, req: Request, res: Response, next: NextFunction) {
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
|
if (error.errorType === 'VIEW_NAME_NOT_UNIQUE') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'VIEW_NAME_NOT_UNIQUE', code: 110 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'RESOURCE_MODEL_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'RESOURCE_MODEL_NOT_FOUND', code: 150, }],
|
||||||
|
});
|
||||||
|
}
|
||||||
if (error.errorType === 'INVALID_LOGIC_EXPRESSION') {
|
if (error.errorType === 'INVALID_LOGIC_EXPRESSION') {
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
errors: [{ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 }],
|
errors: [{ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 }],
|
||||||
@@ -212,6 +244,17 @@ export default class ViewsController extends BaseController{
|
|||||||
errors: [{ type: 'PREDEFINED_VIEW', code: 200 }],
|
errors: [{ type: 'PREDEFINED_VIEW', code: 200 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'RESOURCE_FIELDS_KEYS_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'RESOURCE_FIELDS_KEYS_NOT_FOUND', code: 300 }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (error.errorType === 'RESOURCE_COLUMNS_KEYS_NOT_FOUND') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'RESOURCE_COLUMNS_KEYS_NOT_FOUND', code: 310 }],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
next(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,18 +87,18 @@ export default () => {
|
|||||||
dashboard.use('/accounts', Container.get(Accounts).router());
|
dashboard.use('/accounts', Container.get(Accounts).router());
|
||||||
dashboard.use('/account_types', Container.get(AccountTypes).router());
|
dashboard.use('/account_types', Container.get(AccountTypes).router());
|
||||||
dashboard.use('/manual-journals', Container.get(ManualJournals).router());
|
dashboard.use('/manual-journals', Container.get(ManualJournals).router());
|
||||||
dashboard.use('/views', Views.router());
|
dashboard.use('/views', Container.get(Views).router());
|
||||||
dashboard.use('/items', Container.get(Items).router());
|
dashboard.use('/items', Container.get(Items).router());
|
||||||
dashboard.use('/item_categories', Container.get(ItemCategories).router());
|
dashboard.use('/item_categories', Container.get(ItemCategories).router());
|
||||||
dashboard.use('/expenses', Container.get(Expenses).router());
|
dashboard.use('/expenses', Container.get(Expenses).router());
|
||||||
dashboard.use('/financial_statements', FinancialStatements.router());
|
dashboard.use('/financial_statements', FinancialStatements.router());
|
||||||
dashboard.use('/sales', Container.get(Sales).router());
|
|
||||||
dashboard.use('/customers', Container.get(Customers).router());
|
dashboard.use('/customers', Container.get(Customers).router());
|
||||||
dashboard.use('/vendors', Container.get(Vendors).router());
|
dashboard.use('/vendors', Container.get(Vendors).router());
|
||||||
dashboard.use('/purchases', Container.get(Purchases).router());
|
// dashboard.use('/sales', Container.get(Sales).router());
|
||||||
dashboard.use('/resources', Resources.router());
|
// dashboard.use('/purchases', Container.get(Purchases).router());
|
||||||
|
dashboard.use('/resources', Container.get(Resources).router());
|
||||||
dashboard.use('/exchange_rates', Container.get(ExchangeRates).router());
|
dashboard.use('/exchange_rates', Container.get(ExchangeRates).router());
|
||||||
dashboard.use('/media', Media.router());
|
dashboard.use('/media', Container.get(Media).router());
|
||||||
|
|
||||||
app.use('/', dashboard);
|
app.use('/', dashboard);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export default (req: Request, tenant: ITenant) => {
|
|||||||
const repositories = tenantServices.repositories(tenantId)
|
const repositories = tenantServices.repositories(tenantId)
|
||||||
const cacheInstance = tenantServices.cache(tenantId);
|
const cacheInstance = tenantServices.cache(tenantId);
|
||||||
|
|
||||||
|
tenantServices.setI18nLocals(tenantId, { __: req.__ });
|
||||||
|
|
||||||
req.knex = knexInstance;
|
req.knex = knexInstance;
|
||||||
req.organizationId = organizationId;
|
req.organizationId = organizationId;
|
||||||
req.tenant = tenant;
|
req.tenant = tenant;
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { validationResult } from 'express-validator';
|
|
||||||
|
|
||||||
export default (req, res, next) => {
|
|
||||||
const validationErrors = validationResult(req);
|
|
||||||
|
|
||||||
if (!validationErrors.isEmpty()) {
|
|
||||||
return res.boom.badData(null, {
|
|
||||||
code: 'validation_error',
|
|
||||||
...validationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ export default {
|
|||||||
db_user: process.env.SYSTEM_DB_USER,
|
db_user: process.env.SYSTEM_DB_USER,
|
||||||
db_password: process.env.SYSTEM_DB_PASSWORD,
|
db_password: process.env.SYSTEM_DB_PASSWORD,
|
||||||
db_name: process.env.SYSTEM_DB_NAME,
|
db_name: process.env.SYSTEM_DB_NAME,
|
||||||
|
charset: process.env.SYSTEM_DB_CHARSET,
|
||||||
migrations_dir: process.env.SYSTEM_MIGRATIONS_DIR,
|
migrations_dir: process.env.SYSTEM_MIGRATIONS_DIR,
|
||||||
seeds_dir: process.env.SYSTEM_SEEDS_DIR,
|
seeds_dir: process.env.SYSTEM_SEEDS_DIR,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
// Expenses.
|
// Expenses.
|
||||||
'expenses': {
|
expense: {
|
||||||
payment_date: {
|
payment_date: {
|
||||||
column: 'payment_date',
|
column: 'payment_date',
|
||||||
},
|
},
|
||||||
@@ -10,9 +10,12 @@ export default {
|
|||||||
column: 'payment_account_id',
|
column: 'payment_account_id',
|
||||||
relation: 'accounts.id',
|
relation: 'accounts.id',
|
||||||
},
|
},
|
||||||
total_amount: {
|
amount: {
|
||||||
column: 'total_amount',
|
column: 'total_amount',
|
||||||
},
|
},
|
||||||
|
currency_code: {
|
||||||
|
column: 'currency_code',
|
||||||
|
},
|
||||||
reference_no: {
|
reference_no: {
|
||||||
column: 'reference_no'
|
column: 'reference_no'
|
||||||
},
|
},
|
||||||
@@ -30,7 +33,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
'accounts': {
|
Account: {
|
||||||
name: {
|
name: {
|
||||||
column: 'name',
|
column: 'name',
|
||||||
},
|
},
|
||||||
@@ -72,23 +75,106 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
'items': {
|
item: {
|
||||||
'type': {
|
type: {
|
||||||
column: 'type',
|
column: 'type',
|
||||||
},
|
},
|
||||||
'name': {
|
name: {
|
||||||
column: 'name',
|
column: 'name',
|
||||||
},
|
},
|
||||||
|
sellable: {
|
||||||
|
column: 'sellable',
|
||||||
|
},
|
||||||
|
purchasable: {
|
||||||
|
column: 'purchasable',
|
||||||
|
},
|
||||||
|
sell_price: {
|
||||||
|
column: 'sell_price'
|
||||||
|
},
|
||||||
|
cost_price: {
|
||||||
|
column: 'cost_price',
|
||||||
|
},
|
||||||
|
currency_code: {
|
||||||
|
column: 'currency_code',
|
||||||
|
},
|
||||||
|
cost_account: {
|
||||||
|
column: 'cost_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_account: {
|
||||||
|
column: 'sell_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
inventory_account: {
|
||||||
|
column: 'inventory_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_description: {
|
||||||
|
column: 'sell_description',
|
||||||
|
},
|
||||||
|
purchase_description: {
|
||||||
|
column: 'purchase_description',
|
||||||
|
},
|
||||||
|
quantity_on_hand: {
|
||||||
|
column: 'quantity_on_hand',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
column: 'note',
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
column: 'category_id',
|
||||||
|
relation: 'categories.id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Item category.
|
||||||
|
item_category: {
|
||||||
|
name: {
|
||||||
|
column: 'name',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
column: 'description',
|
||||||
|
},
|
||||||
|
parent_category_id: {
|
||||||
|
column: 'parent_category_id',
|
||||||
|
relation: 'items_categories.id',
|
||||||
|
relationColumn: 'items_categories.id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
cost_account: {
|
||||||
|
column: 'cost_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_account: {
|
||||||
|
column: 'sell_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
inventory_account: {
|
||||||
|
column: 'inventory_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
cost_method: {
|
||||||
|
column: 'cost_method',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Manual Journals
|
// Manual Journals
|
||||||
manual_journals: {
|
manual_journal: {
|
||||||
date: {
|
date: {
|
||||||
column: 'date',
|
column: 'date',
|
||||||
},
|
},
|
||||||
created_at: {
|
|
||||||
column: 'created_at',
|
|
||||||
},
|
|
||||||
journal_number: {
|
journal_number: {
|
||||||
column: 'journal_number',
|
column: 'journal_number',
|
||||||
},
|
},
|
||||||
@@ -112,5 +198,8 @@ export default {
|
|||||||
journal_type: {
|
journal_type: {
|
||||||
column: 'journal_type',
|
column: 'journal_type',
|
||||||
},
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema.createTable('users', (table) => {
|
|
||||||
table.increments();
|
|
||||||
table.string('first_name');
|
|
||||||
table.string('last_name');
|
|
||||||
table.string('email').unique();
|
|
||||||
table.string('phone_number').unique();
|
|
||||||
table.boolean('active');
|
|
||||||
table.integer('role_id').unique();
|
|
||||||
table.string('language');
|
|
||||||
table.date('last_login_at');
|
|
||||||
|
|
||||||
table.date('invite_accepted_at');
|
|
||||||
table.timestamps();
|
|
||||||
}).raw('ALTER TABLE `USERS` AUTO_INCREMENT = 1000');;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = function (knex) {
|
|
||||||
return knex.schema.dropTableIfExists('users');
|
|
||||||
};
|
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
exports.up = (knex) => {
|
exports.up = (knex) => {
|
||||||
return knex.schema.createTable('account_types', (table) => {
|
return knex.schema.createTable('account_types', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('name');
|
table.string('key').index();
|
||||||
table.string('key');
|
table.string('normal').index();
|
||||||
table.string('normal');
|
table.string('root_type').index();
|
||||||
table.string('root_type');
|
|
||||||
table.string('child_type');
|
table.string('child_type');
|
||||||
table.boolean('balance_sheet');
|
table.boolean('balance_sheet');
|
||||||
table.boolean('income_sheet');
|
table.boolean('income_sheet');
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema.createTable('accounts', (table) => {
|
||||||
|
table.increments('id').comment('Auto-generated id');;
|
||||||
|
table.string('name').index();
|
||||||
|
table.string('slug');
|
||||||
|
table.integer('account_type_id').unsigned().references('id').inTable('account_types');
|
||||||
|
table.integer('parent_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
table.string('code', 10).index();
|
||||||
|
table.text('description');
|
||||||
|
table.boolean('active').defaultTo(true).index();
|
||||||
|
table.integer('index').unsigned();
|
||||||
|
table.boolean('predefined').defaultTo(false).index();
|
||||||
|
table.decimal('amount', 15, 5);
|
||||||
|
table.string('currency_code', 3).index();
|
||||||
|
table.timestamps();
|
||||||
|
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = (knex) => knex.schema.dropTableIfExists('accounts');
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema.createTable('items', (table) => {
|
|
||||||
table.increments();
|
|
||||||
table.string('name');
|
|
||||||
table.string('type');
|
|
||||||
table.string('sku');
|
|
||||||
table.boolean('sellable');
|
|
||||||
table.boolean('purchasable');
|
|
||||||
table.decimal('sell_price', 13, 3).unsigned();
|
|
||||||
table.decimal('cost_price', 13, 3).unsigned();
|
|
||||||
table.string('currency_code', 3);
|
|
||||||
table.string('picture_uri');
|
|
||||||
table.integer('cost_account_id').unsigned();
|
|
||||||
table.integer('sell_account_id').unsigned();
|
|
||||||
table.integer('inventory_account_id').unsigned();
|
|
||||||
table.text('sell_description').nullable();
|
|
||||||
table.text('purchase_description').nullable();
|
|
||||||
table.integer('quantity_on_hand');
|
|
||||||
table.text('note').nullable();
|
|
||||||
table.integer('category_id').unsigned();
|
|
||||||
table.integer('user_id').unsigned();
|
|
||||||
table.timestamps();
|
|
||||||
}).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000');;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = (knex) => knex.schema.dropTableIfExists('items');
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema.createTable('accounts', (table) => {
|
|
||||||
table.bigIncrements('id').comment('Auto-generated id');;
|
|
||||||
table.string('name');
|
|
||||||
table.string('slug');
|
|
||||||
table.integer('account_type_id').unsigned();
|
|
||||||
table.integer('parent_account_id').unsigned();
|
|
||||||
table.string('code', 10);
|
|
||||||
table.text('description');
|
|
||||||
table.boolean('active').defaultTo(true);
|
|
||||||
table.integer('index').unsigned();
|
|
||||||
table.boolean('predefined').defaultTo(false);
|
|
||||||
table.decimal('amount', 15, 5);
|
|
||||||
table.string('currency_code', 3);
|
|
||||||
table.timestamps();
|
|
||||||
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = (knex) => knex.schema.dropTableIfExists('accounts');
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema.createTable('items_categories', (table) => {
|
||||||
|
table.increments();
|
||||||
|
table.string('name').index();
|
||||||
|
table.integer('parent_category_id').unsigned().references('id').inTable('items_categories');
|
||||||
|
table.text('description');
|
||||||
|
table.integer('user_id').unsigned().index();
|
||||||
|
|
||||||
|
table.integer('cost_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
table.integer('sell_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
|
||||||
|
table.string('cost_method');
|
||||||
|
table.timestamps();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = (knex) => knex.schema.dropTableIfExists('items_categories');
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema.createTable('items_categories', (table) => {
|
|
||||||
table.increments();
|
|
||||||
table.string('name');
|
|
||||||
table.integer('parent_category_id').unsigned();
|
|
||||||
table.text('description');
|
|
||||||
table.integer('user_id').unsigned();
|
|
||||||
|
|
||||||
table.integer('cost_account_id').unsigned();
|
|
||||||
table.integer('sell_account_id').unsigned();
|
|
||||||
table.integer('inventory_account_id').unsigned();
|
|
||||||
|
|
||||||
table.string('cost_method');
|
|
||||||
table.timestamps();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = (knex) => knex.schema.dropTableIfExists('items_categories');
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema.createTable('items', (table) => {
|
||||||
|
table.increments();
|
||||||
|
table.string('name').index();
|
||||||
|
table.string('type').index();
|
||||||
|
table.string('sku');
|
||||||
|
table.boolean('sellable').index();
|
||||||
|
table.boolean('purchasable').index();
|
||||||
|
table.decimal('sell_price', 13, 3).unsigned();
|
||||||
|
table.decimal('cost_price', 13, 3).unsigned();
|
||||||
|
table.string('currency_code', 3);
|
||||||
|
table.string('picture_uri');
|
||||||
|
table.integer('cost_account_id').nullable().unsigned().references('id').inTable('accounts');
|
||||||
|
table.integer('sell_account_id').nullable().unsigned().references('id').inTable('accounts');
|
||||||
|
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts');
|
||||||
|
table.text('sell_description').nullable();
|
||||||
|
table.text('purchase_description').nullable();
|
||||||
|
table.integer('quantity_on_hand');
|
||||||
|
table.text('note').nullable();
|
||||||
|
table.integer('category_id').unsigned().index().references('id').inTable('items_categories');
|
||||||
|
table.integer('user_id').unsigned().index();
|
||||||
|
table.timestamps();
|
||||||
|
}).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = (knex) => knex.schema.dropTableIfExists('items');
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable('views', (table) => {
|
return knex.schema.createTable('views', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('name');
|
table.string('name').index();
|
||||||
table.boolean('predefined');
|
table.boolean('predefined');
|
||||||
table.string('resource_model');
|
table.string('resource_model').index();
|
||||||
table.boolean('favourite');
|
table.boolean('favourite');
|
||||||
table.string('roles_logic_expression');
|
table.string('roles_logic_expression');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable('settings', (table) => {
|
return knex.schema.createTable('settings', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.string('group');
|
table.string('group').index();
|
||||||
table.string('type');
|
table.string('type');
|
||||||
table.string('key');
|
table.string('key').index();
|
||||||
table.string('value');
|
table.string('value');
|
||||||
}).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
|
}).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable('view_has_columns', (table) => {
|
return knex.schema.createTable('view_has_columns', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('view_id').unsigned();
|
table.integer('view_id').unsigned().index().references('id').inTable('views');
|
||||||
table.string('field_key');
|
table.string('field_key');
|
||||||
table.integer('index').unsigned();
|
table.integer('index').unsigned();
|
||||||
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000');
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ exports.up = function (knex) {
|
|||||||
return knex.schema.createTable('view_roles', (table) => {
|
return knex.schema.createTable('view_roles', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('index');
|
table.integer('index');
|
||||||
table.string('field_key');
|
table.string('field_key').index();
|
||||||
table.string('comparator');
|
table.string('comparator');
|
||||||
table.string('value');
|
table.string('value');
|
||||||
table.integer('view_id').unsigned();
|
table.integer('view_id').unsigned().index().references('id').inTable('views');
|
||||||
}).raw('ALTER TABLE `VIEW_ROLES` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `VIEW_ROLES` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
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');
|
||||||
|
};
|
||||||
@@ -4,17 +4,17 @@ exports.up = function(knex) {
|
|||||||
table.increments();
|
table.increments();
|
||||||
table.decimal('credit', 13, 3);
|
table.decimal('credit', 13, 3);
|
||||||
table.decimal('debit', 13, 3);
|
table.decimal('debit', 13, 3);
|
||||||
table.string('transaction_type');
|
table.string('transaction_type').index();
|
||||||
table.string('reference_type');
|
table.string('reference_type').index();
|
||||||
table.integer('reference_id');
|
table.integer('reference_id').index();
|
||||||
table.integer('account_id').unsigned();
|
table.integer('account_id').unsigned().index().references('id').inTable('accounts');
|
||||||
table.string('contact_type').nullable();
|
table.string('contact_type').nullable().index();
|
||||||
table.integer('contact_id').unsigned().nullable();
|
table.integer('contact_id').unsigned().nullable().index().references('id').inTable('contacts');
|
||||||
table.string('note');
|
table.string('note');
|
||||||
table.boolean('draft').defaultTo(false);
|
table.boolean('draft').defaultTo(false);
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.integer('index').unsigned();
|
table.integer('index').unsigned();
|
||||||
table.date('date');
|
table.date('date').index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function(knex) {
|
|
||||||
return knex.schema.createTable('options', (table) => {
|
|
||||||
table.increments();
|
|
||||||
table.string('key');
|
|
||||||
table.string('value');
|
|
||||||
table.string('group');
|
|
||||||
table.string('type');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = function(knex) {
|
|
||||||
return knex.schema.dropTableIfExists('options');
|
|
||||||
};
|
|
||||||
@@ -5,12 +5,12 @@ exports.up = function(knex) {
|
|||||||
table.decimal('total_amount', 13, 3);
|
table.decimal('total_amount', 13, 3);
|
||||||
table.string('currency_code', 3);
|
table.string('currency_code', 3);
|
||||||
table.text('description');
|
table.text('description');
|
||||||
table.integer('payment_account_id').unsigned();
|
table.integer('payment_account_id').unsigned().references('id').inTable('accounts');
|
||||||
table.integer('payee_id').unsigned();
|
table.integer('payee_id').unsigned().references('id').inTable('contacts');;
|
||||||
table.string('reference_no');
|
table.string('reference_no');
|
||||||
table.date('published_at');
|
table.date('published_at').index();
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.date('payment_date');
|
table.date('payment_date').index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `EXPENSES_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `EXPENSES_TRANSACTIONS` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('manual_journals', (table) => {
|
return knex.schema.createTable('manual_journals', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('journal_number');
|
table.string('journal_number').index();
|
||||||
table.string('reference');
|
table.string('reference').index();
|
||||||
table.string('journal_type');
|
table.string('journal_type').index();
|
||||||
table.decimal('amount', 13, 3);
|
table.decimal('amount', 13, 3);
|
||||||
table.date('date');
|
table.date('date').index();
|
||||||
table.boolean('status').defaultTo(false);
|
table.boolean('status').defaultTo(false).index();
|
||||||
table.string('description');
|
table.string('description');
|
||||||
table.string('attachment_file');
|
table.string('attachment_file');
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('currencies', table => {
|
return knex.schema.createTable('currencies', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('currency_name');
|
table.string('currency_name').index();
|
||||||
table.string('currency_code', 4);
|
table.string('currency_code', 4).index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('exchange_rates', table => {
|
return knex.schema.createTable('exchange_rates', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('currency_code', 4);
|
table.string('currency_code', 4).index();
|
||||||
table.decimal('exchange_rate');
|
table.decimal('exchange_rate');
|
||||||
table.date('date');
|
table.date('date').index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
|
}).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('media_links', table => {
|
return knex.schema.createTable('media_links', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('model_name');
|
table.string('model_name').index();
|
||||||
table.integer('media_id').unsigned();
|
table.integer('media_id').unsigned().references('id').inTable('media');
|
||||||
table.integer('model_id').unsigned();
|
table.integer('model_id').unsigned().index();
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('expense_transaction_categories', table => {
|
return knex.schema.createTable('expense_transaction_categories', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('expense_account_id').unsigned();
|
table.integer('expense_account_id').unsigned().index().references('id').inTable('accounts');
|
||||||
table.integer('index').unsigned();
|
table.integer('index').unsigned();
|
||||||
table.text('description');
|
table.text('description');
|
||||||
table.decimal('amount', 13, 3);
|
table.decimal('amount', 13, 3);
|
||||||
table.integer('expense_id').unsigned();
|
table.integer('expense_id').unsigned().index().references('id').inTable('expenses_transactions');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
}).raw('ALTER TABLE `EXPENSE_TRANSACTION_CATEGORIES` AUTO_INCREMENT = 1000');;
|
}).raw('ALTER TABLE `EXPENSE_TRANSACTION_CATEGORIES` AUTO_INCREMENT = 1000');;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ exports.up = function(knex) {
|
|||||||
return knex.schema.createTable('sales_estimates', (table) => {
|
return knex.schema.createTable('sales_estimates', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.decimal('amount', 13, 3);
|
table.decimal('amount', 13, 3);
|
||||||
table.integer('customer_id').unsigned();
|
table.integer('customer_id').unsigned().index().references('id').inTable('contacts');
|
||||||
table.date('estimate_date');
|
table.date('estimate_date').index();
|
||||||
table.date('expiration_date');
|
table.date('expiration_date').index();
|
||||||
table.string('reference');
|
table.string('reference');
|
||||||
table.string('estimate_number');
|
table.string('estimate_number').index();
|
||||||
table.text('note');
|
table.text('note');
|
||||||
table.text('terms_conditions');
|
table.text('terms_conditions');
|
||||||
|
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ exports.up = function(knex) {
|
|||||||
return knex.schema.createTable('sales_receipts', table => {
|
return knex.schema.createTable('sales_receipts', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.decimal('amount', 13, 3);
|
table.decimal('amount', 13, 3);
|
||||||
table.integer('deposit_account_id').unsigned();
|
table.integer('deposit_account_id').unsigned().index().references('id').inTable('accounts');
|
||||||
table.integer('customer_id').unsigned();
|
table.integer('customer_id').unsigned().index().references('id').inTable('contacts');
|
||||||
table.date('receipt_date');
|
table.date('receipt_date').index();
|
||||||
table.string('reference_no');
|
table.string('reference_no');
|
||||||
table.string('email_send_to');
|
table.string('email_send_to');
|
||||||
table.text('receipt_message');
|
table.text('receipt_message');
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('sales_invoices', table => {
|
return knex.schema.createTable('sales_invoices', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('customer_id');
|
table.integer('customer_id').unsigned().index().references('id').inTable('contacts')
|
||||||
table.date('invoice_date');
|
table.date('invoice_date').index();
|
||||||
table.date('due_date');
|
table.date('due_date');
|
||||||
table.string('invoice_no');
|
table.string('invoice_no').index();
|
||||||
table.string('reference_no');
|
table.string('reference_no');
|
||||||
table.string('status');
|
table.string('status').index();
|
||||||
|
|
||||||
table.text('invoice_message');
|
table.text('invoice_message');
|
||||||
table.text('terms_conditions');
|
table.text('terms_conditions');
|
||||||
@@ -15,7 +15,7 @@ exports.up = function(knex) {
|
|||||||
table.decimal('balance', 13, 3);
|
table.decimal('balance', 13, 3);
|
||||||
table.decimal('payment_amount', 13, 3);
|
table.decimal('payment_amount', 13, 3);
|
||||||
|
|
||||||
table.string('inv_lot_number');
|
table.string('inv_lot_number').index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ const { knexSnakeCaseMappers } = require("objection");
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('payment_receives', (table) => {
|
return knex.schema.createTable('payment_receives', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('customer_id').unsigned();
|
table.integer('customer_id').unsigned().index().references('id').inTable('contacts');
|
||||||
table.date('payment_date');
|
table.date('payment_date').index();
|
||||||
table.decimal('amount', 13, 3).defaultTo(0);
|
table.decimal('amount', 13, 3).defaultTo(0);
|
||||||
table.string('reference_no');
|
table.string('reference_no').index();
|
||||||
table.integer('deposit_account_id').unsigned();
|
table.integer('deposit_account_id').unsigned().references('id').inTable('accounts');
|
||||||
table.string('payment_receive_no');
|
table.string('payment_receive_no');
|
||||||
table.text('description');
|
table.text('description');
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('payment_receives_entries', table => {
|
return knex.schema.createTable('payment_receives_entries', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('payment_receive_id').unsigned();
|
table.integer('payment_receive_id').unsigned().index().references('id').inTable('payment_receives');
|
||||||
table.integer('invoice_id').unsigned();
|
table.integer('invoice_id').unsigned().index().references('id').inTable('sales_invoices');
|
||||||
table.decimal('payment_amount').unsigned();
|
table.decimal('payment_amount').unsigned();
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('bills', (table) => {
|
return knex.schema.createTable('bills', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('vendor_id').unsigned();
|
table.integer('vendor_id').unsigned().index().references('id').inTable('contacts');
|
||||||
table.string('bill_number');
|
table.string('bill_number');
|
||||||
table.date('bill_date');
|
table.date('bill_date').index();
|
||||||
table.date('due_date');
|
table.date('due_date').index();
|
||||||
table.string('reference_no');
|
table.string('reference_no');
|
||||||
table.string('status');
|
table.string('status').index();
|
||||||
table.text('note');
|
table.text('note');
|
||||||
|
|
||||||
table.decimal('amount', 13, 3).defaultTo(0);
|
table.decimal('amount', 13, 3).defaultTo(0);
|
||||||
table.decimal('payment_amount', 13, 3).defaultTo(0);
|
table.decimal('payment_amount', 13, 3).defaultTo(0);
|
||||||
|
|
||||||
table.string('inv_lot_number');
|
table.string('inv_lot_number').index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('bills_payments', table => {
|
return knex.schema.createTable('bills_payments', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.integer('vendor_id').unsigned();
|
table.integer('vendor_id').unsigned().index().references('id').inTable('contacts');
|
||||||
table.decimal('amount', 13, 3).defaultTo(0);
|
table.decimal('amount', 13, 3).defaultTo(0);
|
||||||
table.integer('payment_account_id');
|
table.integer('payment_account_id').unsigned().references('id').inTable('accounts');
|
||||||
table.string('payment_number');
|
table.string('payment_number').index();
|
||||||
table.date('payment_date');
|
table.date('payment_date').index();
|
||||||
table.string('payment_method');
|
table.string('payment_method');
|
||||||
table.string('reference');
|
table.string('reference');
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned().index();
|
||||||
table.text('description');
|
table.text('description');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,20 +2,20 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('inventory_transactions', table => {
|
return knex.schema.createTable('inventory_transactions', table => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
table.date('date');
|
table.date('date').index();
|
||||||
|
|
||||||
table.string('direction');
|
table.string('direction').index();
|
||||||
|
|
||||||
table.integer('item_id').unsigned();
|
table.integer('item_id').unsigned().index().references('id').inTable('items');
|
||||||
table.integer('quantity').unsigned();
|
table.integer('quantity').unsigned();
|
||||||
table.decimal('rate', 13, 3).unsigned();
|
table.decimal('rate', 13, 3).unsigned();
|
||||||
|
|
||||||
table.integer('lot_number');
|
table.integer('lot_number').index();
|
||||||
|
|
||||||
table.string('transaction_type');
|
table.string('transaction_type').index();
|
||||||
table.integer('transaction_id').unsigned();
|
table.integer('transaction_id').unsigned().index();
|
||||||
|
|
||||||
table.integer('entry_id').unsigned();
|
table.integer('entry_id').unsigned().index();
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('items_entries', (table) => {
|
return knex.schema.createTable('items_entries', (table) => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.string('reference_type');
|
table.string('reference_type').index();
|
||||||
table.string('reference_id');
|
table.string('reference_id').index();
|
||||||
|
|
||||||
table.integer('index').unsigned();
|
table.integer('index').unsigned();
|
||||||
table.integer('item_id');
|
table.integer('item_id').unsigned().index().references('id').inTable('items');
|
||||||
table.text('description');
|
table.text('description');
|
||||||
table.integer('discount').unsigned();
|
table.integer('discount').unsigned();
|
||||||
table.integer('quantity').unsigned();
|
table.integer('quantity').unsigned();
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ exports.up = function(knex) {
|
|||||||
return knex.schema.createTable('bills_payments_entries', table => {
|
return knex.schema.createTable('bills_payments_entries', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
|
|
||||||
table.integer('bill_payment_id').unsigned();
|
table.integer('bill_payment_id').unsigned().index().references('id').inTable('bills_payments');
|
||||||
table.integer('bill_id').unsigned();
|
table.integer('bill_id').unsigned().index();
|
||||||
table.decimal('payment_amount', 13, 3).unsigned();
|
table.decimal('payment_amount', 13, 3).unsigned();
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,19 +2,19 @@
|
|||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('inventory_cost_lot_tracker', table => {
|
return knex.schema.createTable('inventory_cost_lot_tracker', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
table.date('date');
|
table.date('date').index();
|
||||||
table.string('direction');
|
table.string('direction').index();
|
||||||
|
|
||||||
table.integer('item_id').unsigned();
|
table.integer('item_id').unsigned().index();
|
||||||
table.integer('quantity').unsigned();
|
table.integer('quantity').unsigned().index();
|
||||||
table.decimal('rate', 13, 3);
|
table.decimal('rate', 13, 3);
|
||||||
table.integer('remaining');
|
table.integer('remaining');
|
||||||
table.integer('cost');
|
table.integer('cost');
|
||||||
table.integer('lot_number');
|
table.integer('lot_number').index();
|
||||||
|
|
||||||
table.string('transaction_type');
|
table.string('transaction_type').index();
|
||||||
table.integer('transaction_id').unsigned();
|
table.integer('transaction_id').unsigned().index();
|
||||||
table.integer('entry_id').unsigned();
|
table.integer('entry_id').unsigned().index();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import Container from 'typedi';
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService'
|
||||||
|
import I18nMiddleware from 'api/middleware/I18nMiddleware';
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
|
const tenancyService = Container.get(TenancyService);
|
||||||
|
const i18n = tenancyService.i18n(knex.userParams.tenantId);
|
||||||
|
|
||||||
return knex('account_types').insert([
|
return knex('account_types').insert([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Fixed Asset',
|
|
||||||
key: 'fixed_asset',
|
key: 'fixed_asset',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'asset',
|
root_type: 'asset',
|
||||||
@@ -12,7 +18,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Current Asset',
|
|
||||||
key: 'current_asset',
|
key: 'current_asset',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'asset',
|
root_type: 'asset',
|
||||||
@@ -22,7 +27,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 14,
|
id: 14,
|
||||||
name: 'Other Asset',
|
|
||||||
key: 'other_asset',
|
key: 'other_asset',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'asset',
|
root_type: 'asset',
|
||||||
@@ -32,7 +36,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Long Term Liability',
|
|
||||||
key: 'long_term_liability',
|
key: 'long_term_liability',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'liability',
|
root_type: 'liability',
|
||||||
@@ -42,7 +45,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'Current Liability',
|
|
||||||
key: 'current_liability',
|
key: 'current_liability',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'liability',
|
root_type: 'liability',
|
||||||
@@ -52,7 +54,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 13,
|
id: 13,
|
||||||
name: 'Other Liability',
|
|
||||||
key: 'other_liability',
|
key: 'other_liability',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'liability',
|
root_type: 'liability',
|
||||||
@@ -62,7 +63,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
name: 'Equity',
|
|
||||||
key: 'equity',
|
key: 'equity',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'equity',
|
root_type: 'equity',
|
||||||
@@ -72,7 +72,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
name: 'Expense',
|
|
||||||
key: 'expense',
|
key: 'expense',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'expense',
|
root_type: 'expense',
|
||||||
@@ -82,7 +81,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 10,
|
id: 10,
|
||||||
name: 'Other Expense',
|
|
||||||
key: 'other_expense',
|
key: 'other_expense',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'expense',
|
root_type: 'expense',
|
||||||
@@ -91,7 +89,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
name: 'Income',
|
|
||||||
key: 'income',
|
key: 'income',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'income',
|
root_type: 'income',
|
||||||
@@ -101,7 +98,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
name: 'Other Income',
|
|
||||||
key: 'other_income',
|
key: 'other_income',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'income',
|
root_type: 'income',
|
||||||
@@ -111,7 +107,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: 12,
|
||||||
name: 'Cost of Goods Sold (COGS)',
|
|
||||||
key: 'cost_of_goods_sold',
|
key: 'cost_of_goods_sold',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'expenses',
|
root_type: 'expenses',
|
||||||
@@ -121,7 +116,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
name: 'Accounts Receivable (A/R)',
|
|
||||||
key: 'accounts_receivable',
|
key: 'accounts_receivable',
|
||||||
normal: 'debit',
|
normal: 'debit',
|
||||||
root_type: 'asset',
|
root_type: 'asset',
|
||||||
@@ -131,7 +125,6 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 9,
|
id: 9,
|
||||||
name: 'Accounts Payable (A/P)',
|
|
||||||
key: 'accounts_payable',
|
key: 'accounts_payable',
|
||||||
normal: 'credit',
|
normal: 'credit',
|
||||||
root_type: 'liability',
|
root_type: 'liability',
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import TenancyService from 'services/Tenancy/TenancyService'
|
|
||||||
import Container from 'typedi';
|
import Container from 'typedi';
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService'
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
const tenancyService = Container.get(TenancyService);
|
const tenancyService = Container.get(TenancyService);
|
||||||
const i18n = tenancyService.i18n(knex.userParams.tenantId);
|
const i18n = tenancyService.i18n(knex.userParams.tenantId);
|
||||||
|
|
||||||
|
console.log(i18n);
|
||||||
|
|
||||||
return knex('accounts').then(() => {
|
return knex('accounts').then(() => {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('accounts').insert([
|
return knex('accounts').insert([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Petty Cash',
|
name: i18n.__('Petty Cash'),
|
||||||
slug: 'petty-cash',
|
slug: 'petty-cash',
|
||||||
account_type_id: 2,
|
account_type_id: 2,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -22,7 +24,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Bank',
|
name: i18n.__('Bank'),
|
||||||
slug: 'bank',
|
slug: 'bank',
|
||||||
account_type_id: 2,
|
account_type_id: 2,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -34,7 +36,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Other Income',
|
name: i18n.__('Other Income'),
|
||||||
slug: 'other-income',
|
slug: 'other-income',
|
||||||
account_type_id: 7,
|
account_type_id: 7,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -46,7 +48,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'Interest Income',
|
name: i18n.__('Interest Income'),
|
||||||
slug: 'interest-income',
|
slug: 'interest-income',
|
||||||
account_type_id: 7,
|
account_type_id: 7,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -58,7 +60,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
name: 'Opening Balance',
|
name: i18n.__('Opening Balance'),
|
||||||
slug: 'opening-balance',
|
slug: 'opening-balance',
|
||||||
account_type_id: 5,
|
account_type_id: 5,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -70,7 +72,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
name: 'Depreciation Expense',
|
name: i18n.__('Depreciation Expense'),
|
||||||
slug: 'depreciation-expense',
|
slug: 'depreciation-expense',
|
||||||
account_type_id: 6,
|
account_type_id: 6,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -82,7 +84,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
name: 'Interest Expense',
|
name: i18n.__('Interest Expense'),
|
||||||
slug: 'interest-expense',
|
slug: 'interest-expense',
|
||||||
account_type_id: 6,
|
account_type_id: 6,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -94,7 +96,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
name: 'Payroll Expenses',
|
name: i18n.__('Payroll Expenses'),
|
||||||
slug: 'payroll-expenses',
|
slug: 'payroll-expenses',
|
||||||
account_type_id: 6,
|
account_type_id: 6,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -106,7 +108,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 9,
|
id: 9,
|
||||||
name: 'Other Expenses',
|
name: i18n.__('Other Expenses'),
|
||||||
slug: 'other-expenses',
|
slug: 'other-expenses',
|
||||||
account_type_id: 6,
|
account_type_id: 6,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -118,7 +120,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 10,
|
id: 10,
|
||||||
name: 'Accounts Receivable',
|
name: i18n.__('Accounts Receivable'),
|
||||||
slug: 'accounts-receivable',
|
slug: 'accounts-receivable',
|
||||||
account_type_id: 8,
|
account_type_id: 8,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -130,7 +132,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
name: 'Accounts Payable',
|
name: i18n.__('Accounts Payable'),
|
||||||
slug: 'accounts-payable',
|
slug: 'accounts-payable',
|
||||||
account_type_id: 9,
|
account_type_id: 9,
|
||||||
parent_account_id: null,
|
parent_account_id: null,
|
||||||
@@ -142,7 +144,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: 12,
|
||||||
name: 'Cost of Goods Sold (COGS)',
|
name: i18n.__('Cost of Goods Sold (COGS)'),
|
||||||
slug: 'cost-of-goods-sold',
|
slug: 'cost-of-goods-sold',
|
||||||
account_type_id: 12,
|
account_type_id: 12,
|
||||||
predefined: 1,
|
predefined: 1,
|
||||||
@@ -153,7 +155,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 13,
|
id: 13,
|
||||||
name: 'Inventory Asset',
|
name: i18n.__('Inventory Asset'),
|
||||||
slug: 'inventory-asset',
|
slug: 'inventory-asset',
|
||||||
account_type_id: 14,
|
account_type_id: 14,
|
||||||
predefined: 1,
|
predefined: 1,
|
||||||
@@ -164,7 +166,7 @@ exports.up = function (knex) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 14,
|
id: 14,
|
||||||
name: 'Sales of Product Income',
|
name: i18n.__('Sales of Product Income'),
|
||||||
slug: 'sales-of-product-income',
|
slug: 'sales-of-product-income',
|
||||||
account_type_id: 7,
|
account_type_id: 7,
|
||||||
predefined: 1,
|
predefined: 1,
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
|
|
||||||
exports.up = (knex) => {
|
exports.up = (knex) => {
|
||||||
|
const tenancyService = Container.get(TenancyService);
|
||||||
|
const i18n = tenancyService.i18n(knex.userParams.tenantId);
|
||||||
|
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
return knex('views').del()
|
return knex('views').del()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('views').insert([
|
return knex('views').insert([
|
||||||
// Accounts
|
// Accounts
|
||||||
{ id: 15, name: 'Inactive', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 15, name: i18n.__('Inactive'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
{ id: 1, name: 'Assets', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 1, name: i18n.__('Assets'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
{ id: 2, name: 'Liabilities', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 2, name: i18n.__('Liabilities'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
{ id: 3, name: 'Equity', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 3, name: i18n.__('Equity'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
{ id: 4, name: 'Income', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 4, name: i18n.__('Income'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
{ id: 5, name: 'Expenses', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
{ id: 5, name: i18n.__('Expenses'), roles_logic_expression: '1', resource_model: 'Account', predefined: true },
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
// { id: 6, name: 'Services', roles_logic_expression: '1', resource_id: 2, predefined: true },
|
// { id: 6, name: 'Services', roles_logic_expression: '1', resource_id: 2, predefined: true },
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ export interface IRegisterDTO {
|
|||||||
organizationName: string,
|
organizationName: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ILoginDTO {
|
||||||
|
crediential: string,
|
||||||
|
password: string,
|
||||||
|
};
|
||||||
|
|
||||||
export interface IPasswordReset {
|
export interface IPasswordReset {
|
||||||
id: number,
|
id: number,
|
||||||
email: string,
|
email: string,
|
||||||
@@ -178,8 +178,9 @@ export interface IVendorsFilter extends IDynamicListFilter {
|
|||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ICustomerFilter extends IDynamicListFilter {
|
export interface ICustomersFilter extends IDynamicListFilter {
|
||||||
stringifiedFilterRoles?: string,
|
stringifiedFilterRoles?: string,
|
||||||
page?: number,
|
page?: number,
|
||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
import { ISystemUser } from "./User";
|
import { ISystemUser } from "./User";
|
||||||
|
|
||||||
|
export interface IPaginationMeta {
|
||||||
|
total: number,
|
||||||
|
page: number,
|
||||||
|
pageSize: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IExpensesFilter{
|
||||||
|
page: number,
|
||||||
|
pageSize: number,
|
||||||
|
};
|
||||||
|
|
||||||
export interface IExpense {
|
export interface IExpense {
|
||||||
id: number,
|
id: number,
|
||||||
totalAmount: number,
|
totalAmount: number,
|
||||||
@@ -53,4 +64,7 @@ export interface IExpensesService {
|
|||||||
deleteBulkExpenses(tenantId: number, expensesIds: number[], authorizedUser: ISystemUser): Promise<void>;
|
deleteBulkExpenses(tenantId: number, expensesIds: number[], authorizedUser: ISystemUser): Promise<void>;
|
||||||
|
|
||||||
publishBulkExpenses(tenantId: number, expensesIds: number[], authorizedUser: ISystemUser): Promise<void>;
|
publishBulkExpenses(tenantId: number, expensesIds: number[], authorizedUser: ISystemUser): Promise<void>;
|
||||||
|
|
||||||
|
getExpensesList(tenantId: number, expensesFilter: IExpensesFilter): Promise<{ expenses: IExpense[], pagination: IPaginationMeta, filterMeta: IFilterMeta }>;
|
||||||
|
getExpense(tenantId: number, expenseId: number): Promise<IExpense>;
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,6 @@ export interface IItemsService {
|
|||||||
|
|
||||||
export interface IItemsFilter extends IDynamicListFilter {
|
export interface IItemsFilter extends IDynamicListFilter {
|
||||||
stringifiedFilterRoles?: string,
|
stringifiedFilterRoles?: string,
|
||||||
page?: number,
|
page: number,
|
||||||
pageSize?: number,
|
pageSize: number,
|
||||||
};
|
};
|
||||||
@@ -37,8 +37,8 @@ export interface IManualJournalDTO {
|
|||||||
|
|
||||||
export interface IManualJournalsFilter extends IDynamicListFilterDTO {
|
export interface IManualJournalsFilter extends IDynamicListFilterDTO {
|
||||||
stringifiedFilterRoles?: string,
|
stringifiedFilterRoles?: string,
|
||||||
page?: number,
|
page: number,
|
||||||
pageSize?: number,
|
pageSize: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IManuaLJournalsService {
|
export interface IManuaLJournalsService {
|
||||||
@@ -48,5 +48,6 @@ export interface IManuaLJournalsService {
|
|||||||
deleteManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void>;
|
deleteManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void>;
|
||||||
publishManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void>;
|
publishManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void>;
|
||||||
publishManualJournal(tenantId: number, manualJournalId: number): Promise<void>;
|
publishManualJournal(tenantId: number, manualJournalId: number): Promise<void>;
|
||||||
getManualJournals(tenantId: number, filter: IManualJournalsFilter): Promise<void>;
|
|
||||||
|
getManualJournals(tenantId: number, filter: IManualJournalsFilter): Promise<{ manualJournals: IManualJournal, pagination: IPaginationMeta, filterMeta: IFilterMeta }>;
|
||||||
}
|
}
|
||||||
25
server/src/interfaces/Media.ts
Normal file
25
server/src/interfaces/Media.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export interface IMedia {
|
||||||
|
id?: number,
|
||||||
|
attachmentFile: string,
|
||||||
|
createdAt?: Date,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IMediaLink {
|
||||||
|
mediaId: number,
|
||||||
|
modelName: string,
|
||||||
|
modelId: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IMediaLinkDTO {
|
||||||
|
modelName: string,
|
||||||
|
modelId: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IMediaService {
|
||||||
|
linkMedia(tenantId: number, mediaId: number, modelId?: number, modelName?: string): Promise<void>;
|
||||||
|
getMedia(tenantId: number, mediaId: number): Promise<IMedia>;
|
||||||
|
deleteMedia(tenantId: number, mediaId: number | number[]): Promise<void>;
|
||||||
|
upload(tenantId: number, attachment: any, modelName?: string, modelId?: number): Promise<IMedia>;
|
||||||
|
}
|
||||||
17
server/src/interfaces/Model.ts
Normal file
17
server/src/interfaces/Model.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export interface IModel {
|
||||||
|
name: string,
|
||||||
|
tableName: string,
|
||||||
|
fields: { [key: string]: any, },
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IFilterMeta {
|
||||||
|
sortOrder: string,
|
||||||
|
sortBy: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IPaginationMeta {
|
||||||
|
pageSize: number,
|
||||||
|
page: number,
|
||||||
|
};
|
||||||
@@ -15,4 +15,9 @@ export interface ISaleInvoiceOTD {
|
|||||||
invoiceMessage: string,
|
invoiceMessage: string,
|
||||||
termsConditions: string,
|
termsConditions: string,
|
||||||
entries: any[],
|
entries: any[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISalesInvoicesFilter{
|
||||||
|
page: number,
|
||||||
|
pageSize: number,
|
||||||
|
};
|
||||||
37
server/src/interfaces/SaleReceipt.ts
Normal file
37
server/src/interfaces/SaleReceipt.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { ISalesInvoicesFilter } from "./SaleInvoice";
|
||||||
|
|
||||||
|
|
||||||
|
export interface ISaleReceipt {
|
||||||
|
id?: number,
|
||||||
|
customerId: number,
|
||||||
|
depositAccountId: number,
|
||||||
|
receiptDate: Date,
|
||||||
|
sendToEmail: string,
|
||||||
|
referenceNo: string,
|
||||||
|
receiptMessage: string,
|
||||||
|
statement: string,
|
||||||
|
entries: any[],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ISalesReceiptsFilter {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ISaleReceiptDTO {
|
||||||
|
customerId: number,
|
||||||
|
depositAccountId: number,
|
||||||
|
receiptDate: Date,
|
||||||
|
sendToEmail: string,
|
||||||
|
referenceNo: string,
|
||||||
|
receiptMessage: string,
|
||||||
|
statement: string,
|
||||||
|
entries: any[],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ISalesReceiptService {
|
||||||
|
createSaleReceipt(tenantId: number, saleReceiptDTO: ISaleReceiptDTO): Promise<void>;
|
||||||
|
editSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
||||||
|
|
||||||
|
deleteSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
||||||
|
salesReceiptsList(tennatid: number, salesReceiptsFilter: ISalesReceiptsFilter): Promise<{ salesReceipts: ISaleReceipt[], pagination: IPaginationMeta, filterMeta: IFilterMeta }>;
|
||||||
|
};
|
||||||
@@ -5,7 +5,10 @@ export interface IView {
|
|||||||
predefined: boolean,
|
predefined: boolean,
|
||||||
resourceModel: string,
|
resourceModel: string,
|
||||||
favourite: boolean,
|
favourite: boolean,
|
||||||
rolesLogicRxpression: string,
|
rolesLogicExpression: string,
|
||||||
|
|
||||||
|
roles: IViewRole[],
|
||||||
|
columns: IViewHasColumn[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IViewRole {
|
export interface IViewRole {
|
||||||
@@ -42,6 +45,8 @@ export interface IViewColumnDTO {
|
|||||||
export interface IViewDTO {
|
export interface IViewDTO {
|
||||||
name: string,
|
name: string,
|
||||||
logicExpression: string,
|
logicExpression: string,
|
||||||
|
resourceModel: string,
|
||||||
|
|
||||||
roles: IViewRoleDTO[],
|
roles: IViewRoleDTO[],
|
||||||
columns: IViewColumnDTO[],
|
columns: IViewColumnDTO[],
|
||||||
};
|
};
|
||||||
@@ -49,12 +54,13 @@ export interface IViewDTO {
|
|||||||
export interface IViewEditDTO {
|
export interface IViewEditDTO {
|
||||||
name: string,
|
name: string,
|
||||||
logicExpression: string,
|
logicExpression: string,
|
||||||
|
|
||||||
roles: IViewRoleDTO[],
|
roles: IViewRoleDTO[],
|
||||||
columns: IViewColumnDTO[],
|
columns: IViewColumnDTO[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IViewsService {
|
export interface IViewsService {
|
||||||
listViews(tenantId: number, resourceModel: string): Promise<void>;
|
listResourceViews(tenantId: number, resourceModel: string): Promise<IView[]>;
|
||||||
newView(tenantId: number, viewDTO: IViewDTO): Promise<void>;
|
newView(tenantId: number, viewDTO: IViewDTO): Promise<void>;
|
||||||
editView(tenantId: number, viewId: number, viewEditDTO: IViewEditDTO): Promise<void>;
|
editView(tenantId: number, viewId: number, viewEditDTO: IViewEditDTO): Promise<void>;
|
||||||
deleteView(tenantId: number, viewId: number): Promise<void>;
|
deleteView(tenantId: number, viewId: number): Promise<void>;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
export * from './Model';
|
||||||
export * from './InventoryTransaction';
|
export * from './InventoryTransaction';
|
||||||
export * from './BillPayment';
|
export * from './BillPayment';
|
||||||
export * from './InventoryCostMethod';
|
export * from './InventoryCostMethod';
|
||||||
@@ -9,7 +11,7 @@ export * from './Payment';
|
|||||||
export * from './SaleInvoice';
|
export * from './SaleInvoice';
|
||||||
export * from './PaymentReceive';
|
export * from './PaymentReceive';
|
||||||
export * from './SaleEstimate';
|
export * from './SaleEstimate';
|
||||||
export * from './Register';
|
export * from './Authentication';
|
||||||
export * from './User';
|
export * from './User';
|
||||||
export * from './Metable';
|
export * from './Metable';
|
||||||
export * from './Options';
|
export * from './Options';
|
||||||
@@ -22,4 +24,5 @@ export * from './Tenancy';
|
|||||||
export * from './View';
|
export * from './View';
|
||||||
export * from './ManualJournal';
|
export * from './ManualJournal';
|
||||||
export * from './Currency';
|
export * from './Currency';
|
||||||
export * from './ExchangeRate';
|
export * from './ExchangeRate';
|
||||||
|
export * from './Media';
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
import { uniqBy } from 'lodash';
|
import { forEach, uniqBy } from 'lodash';
|
||||||
import {
|
import {
|
||||||
buildFilterRolesJoins,
|
buildFilterRolesJoins,
|
||||||
} from 'lib/ViewRolesBuilder';
|
} from 'lib/ViewRolesBuilder';
|
||||||
|
import { IModel } from 'interfaces';
|
||||||
|
|
||||||
export default class DynamicFilter {
|
export default class DynamicFilter {
|
||||||
|
model: IModel;
|
||||||
|
tableName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param {String} tableName -
|
* @param {String} tableName -
|
||||||
*/
|
*/
|
||||||
constructor(tableName) {
|
constructor(model) {
|
||||||
this.tableName = tableName;
|
this.model = model;
|
||||||
|
this.tableName = model.tableName;
|
||||||
this.filters = [];
|
this.filters = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +23,7 @@ export default class DynamicFilter {
|
|||||||
* @param {*} filterRole -
|
* @param {*} filterRole -
|
||||||
*/
|
*/
|
||||||
setFilter(filterRole) {
|
setFilter(filterRole) {
|
||||||
filterRole.setTableName(this.tableName);
|
filterRole.setModel(this.model);
|
||||||
this.filters.push(filterRole);
|
this.filters.push(filterRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +43,23 @@ export default class DynamicFilter {
|
|||||||
buildersCallbacks.forEach((builderCallback) => {
|
buildersCallbacks.forEach((builderCallback) => {
|
||||||
builderCallback(builder);
|
builderCallback(builder);
|
||||||
});
|
});
|
||||||
buildFilterRolesJoins(this.tableName, uniqBy(tableColumns, 'columnKey'))(builder);
|
buildFilterRolesJoins(this.model, uniqBy(tableColumns, 'columnKey'))(builder);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve response metadata from all filters adapters.
|
||||||
|
*/
|
||||||
|
getResponseMeta() {
|
||||||
|
const responseMeta = {};
|
||||||
|
|
||||||
|
this.filters.forEach((filter) => {
|
||||||
|
const { responseMeta: filterMeta } = filter;
|
||||||
|
|
||||||
|
forEach(filterMeta, (value, key) => {
|
||||||
|
responseMeta[key] = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return responseMeta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,14 +14,14 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
|||||||
constructor(filterRoles: IFilterRole[]) {
|
constructor(filterRoles: IFilterRole[]) {
|
||||||
super();
|
super();
|
||||||
this.filterRoles = filterRoles;
|
this.filterRoles = filterRoles;
|
||||||
|
this.setResponseMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildLogicExpression(): string {
|
private buildLogicExpression(): string {
|
||||||
let expression = '';
|
let expression = '';
|
||||||
this.filterRoles.forEach((role, index) => {
|
this.filterRoles.forEach((role, index) => {
|
||||||
expression += (index === 0) ?
|
expression += (index === 0) ?
|
||||||
`${role.index} ` :
|
`${role.index} ` : `${role.condition} ${role.index} `;
|
||||||
`${role.condition} ${role.index} `;
|
|
||||||
});
|
});
|
||||||
return expression.trim();
|
return expression.trim();
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,16 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
|||||||
buildQuery() {
|
buildQuery() {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
const logicExpression = this.buildLogicExpression();
|
const logicExpression = this.buildLogicExpression();
|
||||||
buildFilterQuery(this.tableName, this.filterRoles, logicExpression)(builder);
|
buildFilterQuery(this.model, this.filterRoles, logicExpression)(builder);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets response meta.
|
||||||
|
*/
|
||||||
|
setResponseMeta() {
|
||||||
|
this.responseMeta = {
|
||||||
|
filterRoles: this.filterRoles
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import { IFilterRole, IDynamicFilter } from "interfaces";
|
import { IFilterRole, IDynamicFilter, IModel } from "interfaces";
|
||||||
|
|
||||||
export default class DynamicFilterAbstructor implements IDynamicFilter {
|
export default class DynamicFilterAbstructor implements IDynamicFilter {
|
||||||
filterRoles: IFilterRole[] = [];
|
filterRoles: IFilterRole[] = [];
|
||||||
tableName: string;
|
tableName: string;
|
||||||
|
model: IModel;
|
||||||
|
responseMeta: { [key: string]: any } = {};
|
||||||
|
|
||||||
setTableName(tableName) {
|
setModel(model: IModel) {
|
||||||
this.tableName = tableName;
|
this.model = model;
|
||||||
|
this.tableName = model.tableName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,11 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
|||||||
fieldKey: sortByFieldKey,
|
fieldKey: sortByFieldKey,
|
||||||
order: sortDirection,
|
order: sortDirection,
|
||||||
};
|
};
|
||||||
|
this.setResponseMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
validateFieldKeyExistance(this.tableName, this.sortRole.fieldKey);
|
validateFieldKeyExistance(this.model, this.sortRole.fieldKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +28,7 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
|||||||
*/
|
*/
|
||||||
buildQuery() {
|
buildQuery() {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
const fieldRelation = getRoleFieldColumn(this.tableName, this.sortRole.fieldKey);
|
const fieldRelation = getRoleFieldColumn(this.model, this.sortRole.fieldKey);
|
||||||
const comparatorColumn =
|
const comparatorColumn =
|
||||||
fieldRelation.relationColumn ||
|
fieldRelation.relationColumn ||
|
||||||
`${this.tableName}.${fieldRelation.column}`;
|
`${this.tableName}.${fieldRelation.column}`;
|
||||||
@@ -37,4 +38,14 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets response meta.
|
||||||
|
*/
|
||||||
|
setResponseMeta() {
|
||||||
|
this.responseMeta = {
|
||||||
|
sortOrder: this.sortRole.fieldKey,
|
||||||
|
sortBy: this.sortRole.order,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
import { IFilterRole } from 'interfaces';
|
import { omit } from 'lodash';
|
||||||
|
import { IView, IViewRole } from 'interfaces';
|
||||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||||
import {
|
import {
|
||||||
validateViewRoles,
|
|
||||||
buildFilterQuery,
|
buildFilterQuery,
|
||||||
} from 'lib/ViewRolesBuilder';
|
} from 'lib/ViewRolesBuilder';
|
||||||
|
|
||||||
export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||||
|
viewId: number;
|
||||||
logicExpression: string;
|
logicExpression: string;
|
||||||
|
filterRoles: IViewRole[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor method.
|
* Constructor method.
|
||||||
* @param {*} filterRoles - Filter roles.
|
* @param {IView} view -
|
||||||
* @param {*} logicExpression - Logic expression.
|
|
||||||
*/
|
*/
|
||||||
constructor(filterRoles: IFilterRole[], logicExpression: string) {
|
constructor(view: IView) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.filterRoles = filterRoles;
|
this.viewId = view.id;
|
||||||
this.logicExpression = logicExpression
|
this.filterRoles = view.roles;
|
||||||
|
this.logicExpression = view.rolesLogicExpression
|
||||||
.replace('AND', '&&')
|
.replace('AND', '&&')
|
||||||
.replace('OR', '||');
|
.replace('OR', '||');
|
||||||
|
|
||||||
|
this.setResponseMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,20 +32,27 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
|||||||
buildLogicExpression() {
|
buildLogicExpression() {
|
||||||
return this.logicExpression;
|
return this.logicExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates filter roles.
|
|
||||||
*/
|
|
||||||
validate() {
|
|
||||||
return validateViewRoles(this.filterRoles, this.logicExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds database query of view roles.
|
* Builds database query of view roles.
|
||||||
*/
|
*/
|
||||||
buildQuery() {
|
buildQuery() {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
buildFilterQuery(this.tableName, this.filterRoles, this.logicExpression)(builder);
|
buildFilterQuery(this.model, this.filterRoles, this.logicExpression)(builder);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets response meta.
|
||||||
|
*/
|
||||||
|
setResponseMeta() {
|
||||||
|
this.responseMeta = {
|
||||||
|
view: {
|
||||||
|
logicExpression: this.logicExpression,
|
||||||
|
filterRoles: this.filterRoles
|
||||||
|
.map((filterRole) => ({ ...omit(filterRole, ['id', 'viewId']) })),
|
||||||
|
customViewId: this.viewId,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { difference, filter } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Lexer } from 'lib/LogicEvaluation/Lexer';
|
import { Lexer } from 'lib/LogicEvaluation/Lexer';
|
||||||
import Parser from 'lib/LogicEvaluation/Parser';
|
import Parser from 'lib/LogicEvaluation/Parser';
|
||||||
import QueryParser from 'lib/LogicEvaluation/QueryParser';
|
import QueryParser from 'lib/LogicEvaluation/QueryParser';
|
||||||
import resourceFieldsKeys from 'data/ResourceFieldsKeys';
|
import { IFilterRole, IModel } from 'interfaces';
|
||||||
import { IFilterRole } from 'interfaces';
|
|
||||||
|
|
||||||
const numberRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
const numberRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||||
switch (role.comparator) {
|
switch (role.comparator) {
|
||||||
@@ -93,7 +92,7 @@ const dateQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
|||||||
if (hasTimeFormat) {
|
if (hasTimeFormat) {
|
||||||
const targetDateTime = moment(role.value).format(dateFormat);
|
const targetDateTime = moment(role.value).format(dateFormat);
|
||||||
builder.where(comparatorColumn, '=', targetDateTime);
|
builder.where(comparatorColumn, '=', targetDateTime);
|
||||||
} else {
|
} else {
|
||||||
const startDate = moment(role.value).startOf('day');
|
const startDate = moment(role.value).startOf('day');
|
||||||
const endDate = moment(role.value).endOf('day');
|
const endDate = moment(role.value).endOf('day');
|
||||||
|
|
||||||
@@ -109,19 +108,19 @@ const dateQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
|||||||
* @param {String} tableName - Table name of target column.
|
* @param {String} tableName - Table name of target column.
|
||||||
* @param {String} fieldKey - Target column key that stored in resource field.
|
* @param {String} fieldKey - Target column key that stored in resource field.
|
||||||
*/
|
*/
|
||||||
export function getRoleFieldColumn(tableName: string, fieldKey: string) {
|
export function getRoleFieldColumn(model: IModel, fieldKey: string) {
|
||||||
const tableFields = resourceFieldsKeys[tableName];
|
const tableFields = model.fields;
|
||||||
return (tableFields[fieldKey]) ? tableFields[fieldKey] : null;
|
return (tableFields[fieldKey]) ? tableFields[fieldKey] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds roles queries.
|
* Builds roles queries.
|
||||||
* @param {String} tableName -
|
* @param {IModel} model -
|
||||||
* @param {Object} role -
|
* @param {Object} role -
|
||||||
*/
|
*/
|
||||||
export function buildRoleQuery(tableName: string, role: IFilterRole) {
|
export function buildRoleQuery(model: IModel, role: IFilterRole) {
|
||||||
const fieldRelation = getRoleFieldColumn(tableName, role.fieldKey);
|
const fieldRelation = getRoleFieldColumn(model, role.fieldKey);
|
||||||
const comparatorColumn = fieldRelation.relationColumn || `${tableName}.${fieldRelation.column}`;
|
const comparatorColumn = fieldRelation.relationColumn || `${model.tableName}.${fieldRelation.column}`;
|
||||||
|
|
||||||
switch (fieldRelation.columnType) {
|
switch (fieldRelation.columnType) {
|
||||||
case 'number':
|
case 'number':
|
||||||
@@ -150,26 +149,26 @@ export const getTableFromRelationColumn = (column: string) => {
|
|||||||
* @param {String} tableName -
|
* @param {String} tableName -
|
||||||
* @param {Array} roles -
|
* @param {Array} roles -
|
||||||
*/
|
*/
|
||||||
export function buildFilterRolesJoins(tableName: string, roles: IFilterRole[]) {
|
export function buildFilterRolesJoins(model: IModel, roles: IFilterRole[]) {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
roles.forEach((role) => {
|
roles.forEach((role) => {
|
||||||
const fieldColumn = getRoleFieldColumn(tableName, role.fieldKey);
|
const fieldColumn = getRoleFieldColumn(model, role.fieldKey);
|
||||||
|
|
||||||
if (fieldColumn.relation) {
|
if (fieldColumn.relation) {
|
||||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||||
builder.join(joinTable, `${tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
builder.join(joinTable, `${model.tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildSortColumnJoin(tableName: string, sortColumnKey: string) {
|
export function buildSortColumnJoin(model: IModel, sortColumnKey: string) {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
const fieldColumn = getRoleFieldColumn(tableName, sortColumnKey);
|
const fieldColumn = getRoleFieldColumn(model, sortColumnKey);
|
||||||
|
|
||||||
if (fieldColumn.relation) {
|
if (fieldColumn.relation) {
|
||||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||||
builder.join(joinTable, `${tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
builder.join(joinTable, `${model.tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -180,11 +179,11 @@ export function buildSortColumnJoin(tableName: string, sortColumnKey: string) {
|
|||||||
* @param {Array} roles -
|
* @param {Array} roles -
|
||||||
* @return {Function}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
export function buildFilterRolesQuery(tableName: string, roles: IFilterRole[], logicExpression: string = '') {
|
export function buildFilterRolesQuery(model: IModel, roles: IFilterRole[], logicExpression: string = '') {
|
||||||
const rolesIndexSet = {};
|
const rolesIndexSet = {};
|
||||||
|
|
||||||
roles.forEach((role) => {
|
roles.forEach((role) => {
|
||||||
rolesIndexSet[role.index] = buildRoleQuery(tableName, role);
|
rolesIndexSet[role.index] = buildRoleQuery(model, role);
|
||||||
});
|
});
|
||||||
// Lexer for logic expression.
|
// Lexer for logic expression.
|
||||||
const lexer = new Lexer(logicExpression);
|
const lexer = new Lexer(logicExpression);
|
||||||
@@ -204,9 +203,9 @@ export function buildFilterRolesQuery(tableName: string, roles: IFilterRole[], l
|
|||||||
* @param {Array} roles -
|
* @param {Array} roles -
|
||||||
* @param {String} logicExpression -
|
* @param {String} logicExpression -
|
||||||
*/
|
*/
|
||||||
export const buildFilterQuery = (tableName: string, roles, logicExpression: string) => {
|
export const buildFilterQuery = (model: IModel, roles: IFilterRole[], logicExpression: string) => {
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
buildFilterRolesQuery(tableName, roles, logicExpression)(builder);
|
buildFilterRolesQuery(model, roles, logicExpression)(builder);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,35 +239,33 @@ export function mapFilterRolesToDynamicFilter(roles) {
|
|||||||
* @param {String} columnKey -
|
* @param {String} columnKey -
|
||||||
* @param {String} sortDirection -
|
* @param {String} sortDirection -
|
||||||
*/
|
*/
|
||||||
export function buildSortColumnQuery(tableName: string, columnKey: string, sortDirection: string) {
|
export function buildSortColumnQuery(model: IModel, columnKey: string, sortDirection: string) {
|
||||||
const fieldRelation = getRoleFieldColumn(tableName, columnKey);
|
const fieldRelation = getRoleFieldColumn(model, columnKey);
|
||||||
const sortColumn = fieldRelation.relation || `${tableName}.${fieldRelation.column}`;
|
const sortColumn = fieldRelation.relation || `${model.tableName}.${fieldRelation.column}`;
|
||||||
|
|
||||||
return (builder) => {
|
return (builder) => {
|
||||||
builder.orderBy(sortColumn, sortDirection);
|
builder.orderBy(sortColumn, sortDirection);
|
||||||
buildSortColumnJoin(tableName, columnKey)(builder);
|
buildSortColumnJoin(model, columnKey)(builder);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateFilterLogicExpression(logicExpression: string, indexes) {
|
export function validateFilterLogicExpression(logicExpression: string, indexes) {
|
||||||
const logicExpIndexes = logicExpression.match(/\d+/g) || [];
|
const logicExpIndexes = logicExpression.match(/\d+/g) || [];
|
||||||
const diff = !difference(logicExpIndexes.map(Number), indexes).length;
|
const diff = difference(logicExpIndexes.map(Number), indexes);
|
||||||
|
|
||||||
|
return (diff.length > 0) ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateRolesLogicExpression(logicExpression: string, roles: IFilterRole[]) {
|
export function validateRolesLogicExpression(logicExpression: string, roles: IFilterRole[]) {
|
||||||
return validateFilterLogicExpression(logicExpression, roles.map((r) => r.index));
|
return validateFilterLogicExpression(logicExpression, roles.map((r) => r.index));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateFieldKeyExistance(tableName: string, fieldKey: string) {
|
export function validateFieldKeyExistance(model: any, fieldKey: string) {
|
||||||
if (!resourceFieldsKeys?.[tableName]?.[fieldKey])
|
return model?.fields?.[fieldKey] || false;
|
||||||
return fieldKey;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateFilterRolesFieldsExistance(tableName, filterRoles: IFilterRole[]) {
|
export function validateFilterRolesFieldsExistance(model, filterRoles: IFilterRole[]) {
|
||||||
return filterRoles.filter((filterRole: IFilterRole) => {
|
return filterRoles.filter((filterRole: IFilterRole) => {
|
||||||
return validateFieldKeyExistance(tableName, filterRole.fieldKey);
|
return !validateFieldKeyExistance(model, filterRole.fieldKey);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
// Here we import all events.
|
// Here we import all events.
|
||||||
import 'subscribers/authentication';
|
import 'subscribers/authentication';
|
||||||
import 'subscribers/organization';
|
import 'subscribers/organization';
|
||||||
import 'subscribers/manualJournals';
|
import 'subscribers/manualJournals';
|
||||||
|
import 'subscribers/expenses';
|
||||||
@@ -4,6 +4,6 @@ import path from 'path';
|
|||||||
export default () => i18n.configure({
|
export default () => i18n.configure({
|
||||||
locales: ['en', 'ar'],
|
locales: ['en', 'ar'],
|
||||||
register: global,
|
register: global,
|
||||||
directory: path.join(global.__root, 'src/locales'),
|
directory: path.join(global.__root, 'locales'),
|
||||||
updateFiles: false
|
updateFiles: false
|
||||||
})
|
})
|
||||||
@@ -10,9 +10,7 @@ import Bill from 'models/Bill';
|
|||||||
import BillPayment from 'models/BillPayment';
|
import BillPayment from 'models/BillPayment';
|
||||||
import BillPaymentEntry from 'models/BillPaymentEntry';
|
import BillPaymentEntry from 'models/BillPaymentEntry';
|
||||||
import Currency from 'models/Currency';
|
import Currency from 'models/Currency';
|
||||||
import Customer from 'models/Customer';
|
|
||||||
import Contact from 'models/Contact';
|
import Contact from 'models/Contact';
|
||||||
import Vendor from 'models/Vendor';
|
|
||||||
import ExchangeRate from 'models/ExchangeRate';
|
import ExchangeRate from 'models/ExchangeRate';
|
||||||
import Expense from 'models/Expense';
|
import Expense from 'models/Expense';
|
||||||
import ExpenseCategory from 'models/ExpenseCategory';
|
import ExpenseCategory from 'models/ExpenseCategory';
|
||||||
@@ -49,8 +47,6 @@ export default (knex) => {
|
|||||||
BillPayment,
|
BillPayment,
|
||||||
BillPaymentEntry,
|
BillPaymentEntry,
|
||||||
Currency,
|
Currency,
|
||||||
Customer,
|
|
||||||
Vendor,
|
|
||||||
ExchangeRate,
|
ExchangeRate,
|
||||||
Expense,
|
Expense,
|
||||||
ExpenseCategory,
|
ExpenseCategory,
|
||||||
|
|||||||
@@ -1,4 +1,33 @@
|
|||||||
{
|
{
|
||||||
"Empty": "",
|
"Empty": "",
|
||||||
"Hello": "Hello"
|
"Hello": "Hello",
|
||||||
|
"Petty Cash": "Petty Cash 2",
|
||||||
|
"Bank": "Bank",
|
||||||
|
"Other Income": "Other Income",
|
||||||
|
"Interest Income": "Interest Income",
|
||||||
|
"Opening Balance": "Opening Balance",
|
||||||
|
"Depreciation Expense": "Depreciation Expense",
|
||||||
|
"Interest Expense": "Interest Expense",
|
||||||
|
"Sales of Product Income": "Sales of Product Income",
|
||||||
|
"Inventory Asset": "Inventory Asset",
|
||||||
|
"Cost of Goods Sold (COGS)": "Cost of Goods Sold (COGS)",
|
||||||
|
"Accounts Payable": "Accounts Payable",
|
||||||
|
"Other Expenses": "Other Expenses",
|
||||||
|
"Payroll Expenses": "Payroll Expenses",
|
||||||
|
"Fixed Asset": "Fixed Asset",
|
||||||
|
"Current Asset": "Current Asset",
|
||||||
|
"Other Asset": "Other Asset",
|
||||||
|
"Long Term Liability": "Long Term Liability",
|
||||||
|
"Current Liability": "Current Liability",
|
||||||
|
"Other Liability": "Other Liability",
|
||||||
|
"Equity": "Equity",
|
||||||
|
"Expense": "Expense",
|
||||||
|
"Other Expense": "Other Expense",
|
||||||
|
"Income": "Income",
|
||||||
|
"Accounts Receivable (A/R)": "Accounts Receivable (A/R)",
|
||||||
|
"Accounts Payable (A/P)": "Accounts Payable (A/P)",
|
||||||
|
"Inactive": "Inactive",
|
||||||
|
"Assets": "Assets",
|
||||||
|
"Liabilities": "Liabilities",
|
||||||
|
"Expenses": "Expenses",
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from 'lib/ViewRolesBuilder';
|
} from 'lib/ViewRolesBuilder';
|
||||||
import { flatToNestedArray } from 'utils';
|
import { flatToNestedArray } from 'utils';
|
||||||
import DependencyGraph from 'lib/DependencyGraph';
|
import DependencyGraph from 'lib/DependencyGraph';
|
||||||
|
import TenantManagerSubscriber from 'subscribers/tenantManager';
|
||||||
|
|
||||||
export default class Account extends TenantModel {
|
export default class Account extends TenantModel {
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +25,13 @@ export default class Account extends TenantModel {
|
|||||||
return ['createdAt', 'updatedAt'];
|
return ['createdAt', 'updatedAt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static get resourceable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model modifiers.
|
* Model modifiers.
|
||||||
*/
|
*/
|
||||||
@@ -106,4 +114,55 @@ export default class Account extends TenantModel {
|
|||||||
accounts, { itemId: 'id', parentItemId: 'parentAccountId' }
|
accounts, { itemId: 'id', parentItemId: 'parentAccountId' }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model defined fields.
|
||||||
|
*/
|
||||||
|
static get fields() {
|
||||||
|
return {
|
||||||
|
name: {
|
||||||
|
label: 'Name',
|
||||||
|
column: 'name',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
label: 'Account type',
|
||||||
|
column: 'account_type_id',
|
||||||
|
relation: 'account_types.id',
|
||||||
|
relationColumn: 'account_types.key',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
label: 'Description',
|
||||||
|
column: 'description',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
label: 'Account code',
|
||||||
|
column: 'code',
|
||||||
|
},
|
||||||
|
root_type: {
|
||||||
|
label: 'Type',
|
||||||
|
column: 'account_type_id',
|
||||||
|
relation: 'account_types.id',
|
||||||
|
relationColumn: 'account_types.root_type',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
columnType: 'date',
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
column: 'active',
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
column: 'amount',
|
||||||
|
columnType: 'number'
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
column: 'currency_code',
|
||||||
|
},
|
||||||
|
normal: {
|
||||||
|
column: 'account_type_id',
|
||||||
|
relation: 'account_types.id',
|
||||||
|
relationColumn: 'account_types.normal'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,17 +36,20 @@ export default class Bill extends TenantModel {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Vendor = require('models/Vendor');
|
const Contact = require('models/Contact');
|
||||||
const ItemEntry = require('models/ItemEntry');
|
const ItemEntry = require('models/ItemEntry');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vendor: {
|
vendor: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Vendor.default,
|
modelClass: Contact.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'bills.vendorId',
|
from: 'bills.vendorId',
|
||||||
to: 'vendors.id',
|
to: 'contacts.id',
|
||||||
},
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('contact_type', 'Vendor');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entries: {
|
entries: {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default class BillPayment extends TenantModel {
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const BillPaymentEntry = require('models/BillPaymentEntry');
|
const BillPaymentEntry = require('models/BillPaymentEntry');
|
||||||
const AccountTransaction = require('models/AccountTransaction');
|
const AccountTransaction = require('models/AccountTransaction');
|
||||||
const Vendor = require('models/Vendor');
|
const Contact = require('models/Contact');
|
||||||
const Account = require('models/Account');
|
const Account = require('models/Account');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -37,11 +37,14 @@ export default class BillPayment extends TenantModel {
|
|||||||
|
|
||||||
vendor: {
|
vendor: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Vendor.default,
|
modelClass: Contact.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'bills_payments.vendorId',
|
from: 'bills_payments.vendorId',
|
||||||
to: 'vendors.id',
|
to: 'contacts.id',
|
||||||
},
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('contact_type', 'Vendor');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
paymentAccount: {
|
paymentAccount: {
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
import { Model } from 'objection';
|
|
||||||
import TenantModel from 'models/TenantModel';
|
|
||||||
|
|
||||||
export default class Customer extends TenantModel {
|
|
||||||
/**
|
|
||||||
* Table name
|
|
||||||
*/
|
|
||||||
static get tableName() {
|
|
||||||
return 'customers';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model timestamps.
|
|
||||||
*/
|
|
||||||
get timestamps() {
|
|
||||||
return ['createdAt', 'updatedAt'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model modifiers.
|
|
||||||
*/
|
|
||||||
static get modifiers() {
|
|
||||||
return {
|
|
||||||
filterCustomerIds(query, customerIds) {
|
|
||||||
query.whereIn('id', customerIds);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change vendor balance.
|
|
||||||
* @param {Integer} customerId
|
|
||||||
* @param {Numeric} amount
|
|
||||||
*/
|
|
||||||
static async changeBalance(customerId, amount) {
|
|
||||||
const changeMethod = (amount > 0) ? 'increment' : 'decrement';
|
|
||||||
|
|
||||||
return this.query()
|
|
||||||
.where('id', customerId)
|
|
||||||
[changeMethod]('balance', Math.abs(amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment the given customer balance.
|
|
||||||
* @param {Integer} customerId
|
|
||||||
* @param {Integer} amount
|
|
||||||
*/
|
|
||||||
static async incrementBalance(customerId, amount) {
|
|
||||||
return this.query()
|
|
||||||
.where('id', customerId)
|
|
||||||
.increment('balance', amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrement the given customer balance.
|
|
||||||
* @param {integer} customerId -
|
|
||||||
* @param {integer} amount -
|
|
||||||
*/
|
|
||||||
static async decrementBalance(customerId, amount) {
|
|
||||||
await this.query()
|
|
||||||
.where('id', customerId)
|
|
||||||
.decrement('balance', amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
static changeDiffBalance(customerId, oldCustomerId, amount, oldAmount) {
|
|
||||||
const diffAmount = amount - oldAmount;
|
|
||||||
const asyncOpers = [];
|
|
||||||
|
|
||||||
if (customerId != oldCustomerId) {
|
|
||||||
const oldCustomerOper = this.changeBalance(oldCustomerId, (oldAmount * -1));
|
|
||||||
const customerOper = this.changeBalance(customerId, amount);
|
|
||||||
|
|
||||||
asyncOpers.push(customerOper);
|
|
||||||
asyncOpers.push(oldCustomerOper);
|
|
||||||
} else {
|
|
||||||
const balanceChangeOper = this.changeBalance(customerId, diffAmount);
|
|
||||||
asyncOpers.push(balanceChangeOper);
|
|
||||||
}
|
|
||||||
return Promise.all(asyncOpers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import TenantModel from 'models/TenantModel';
|
import TenantModel from 'models/TenantModel';
|
||||||
import { viewRolesBuilder } from 'lib/ViewRolesBuilder';
|
import { viewRolesBuilder } from 'lib/ViewRolesBuilder';
|
||||||
|
import Media from './Media';
|
||||||
|
|
||||||
export default class Expense extends TenantModel {
|
export default class Expense extends TenantModel {
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +25,11 @@ export default class Expense extends TenantModel {
|
|||||||
return ['createdAt', 'updatedAt'];
|
return ['createdAt', 'updatedAt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static get media () {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model modifiers.
|
* Model modifiers.
|
||||||
*/
|
*/
|
||||||
@@ -55,14 +61,9 @@ export default class Expense extends TenantModel {
|
|||||||
query.where('payment_account_id', accountId);
|
query.where('payment_account_id', accountId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
viewRolesBuilder(query, conditionals, expression) {
|
viewRolesBuilder(query, conditionals, expression) {
|
||||||
viewRolesBuilder(conditionals, expression)(query);
|
viewRolesBuilder(conditionals, expression)(query);
|
||||||
},
|
},
|
||||||
|
|
||||||
orderBy(query) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ export default class Expense extends TenantModel {
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const Account = require('models/Account');
|
const Account = require('models/Account');
|
||||||
const ExpenseCategory = require('models/ExpenseCategory');
|
const ExpenseCategory = require('models/ExpenseCategory');
|
||||||
const SystemUser = require('system/models/SystemUser');
|
const Media = require('models/Media');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentAccount: {
|
paymentAccount: {
|
||||||
@@ -91,14 +92,59 @@ export default class Expense extends TenantModel {
|
|||||||
to: 'expense_transaction_categories.expenseId',
|
to: 'expense_transaction_categories.expenseId',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
user: {
|
media: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: SystemUser.default,
|
modelClass: Media.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'expenses_transactions.userId',
|
from: 'expenses_transactions.id',
|
||||||
to: 'users.id',
|
through: {
|
||||||
|
from: 'media_links.model_id',
|
||||||
|
to: 'media_links.media_id',
|
||||||
|
},
|
||||||
|
to: 'media.id',
|
||||||
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('model_name', 'Expense');
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model defined fields.
|
||||||
|
*/
|
||||||
|
static get fields() {
|
||||||
|
return {
|
||||||
|
payment_date: {
|
||||||
|
column: 'payment_date',
|
||||||
|
},
|
||||||
|
payment_account: {
|
||||||
|
column: 'payment_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
column: 'total_amount',
|
||||||
|
},
|
||||||
|
currency_code: {
|
||||||
|
column: 'currency_code',
|
||||||
|
},
|
||||||
|
reference_no: {
|
||||||
|
column: 'reference_no'
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
column: 'description',
|
||||||
|
},
|
||||||
|
published: {
|
||||||
|
column: 'published',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,4 +95,67 @@ export default class Item extends TenantModel {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static get fields() {
|
||||||
|
return {
|
||||||
|
type: {
|
||||||
|
column: 'type',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
column: 'name',
|
||||||
|
},
|
||||||
|
sellable: {
|
||||||
|
column: 'sellable',
|
||||||
|
},
|
||||||
|
purchasable: {
|
||||||
|
column: 'purchasable',
|
||||||
|
},
|
||||||
|
sell_price: {
|
||||||
|
column: 'sell_price'
|
||||||
|
},
|
||||||
|
cost_price: {
|
||||||
|
column: 'cost_price',
|
||||||
|
},
|
||||||
|
currency_code: {
|
||||||
|
column: 'currency_code',
|
||||||
|
},
|
||||||
|
cost_account: {
|
||||||
|
column: 'cost_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_account: {
|
||||||
|
column: 'sell_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
inventory_account: {
|
||||||
|
column: 'inventory_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_description: {
|
||||||
|
column: 'sell_description',
|
||||||
|
},
|
||||||
|
purchase_description: {
|
||||||
|
column: 'purchase_description',
|
||||||
|
},
|
||||||
|
quantity_on_hand: {
|
||||||
|
column: 'quantity_on_hand',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
column: 'note',
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
column: 'category_id',
|
||||||
|
relation: 'categories.id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default class ItemCategory extends TenantModel {
|
|||||||
return 'items_categories';
|
return 'items_categories';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get resourceable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamps columns.
|
* Timestamps columns.
|
||||||
*/
|
*/
|
||||||
@@ -37,4 +41,43 @@ export default class ItemCategory extends TenantModel {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get fields() {
|
||||||
|
return {
|
||||||
|
name: {
|
||||||
|
column: 'name',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
column: 'description',
|
||||||
|
},
|
||||||
|
parent_category_id: {
|
||||||
|
column: 'parent_category_id',
|
||||||
|
relation: 'items_categories.id',
|
||||||
|
relationColumn: 'items_categories.id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
cost_account: {
|
||||||
|
column: 'cost_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
sell_account: {
|
||||||
|
column: 'sell_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
inventory_account: {
|
||||||
|
column: 'inventory_account_id',
|
||||||
|
relation: 'accounts.id',
|
||||||
|
},
|
||||||
|
cost_method: {
|
||||||
|
column: 'cost_method',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,48 @@ export default class ManualJournal extends TenantModel {
|
|||||||
to: 'media_links.media_id',
|
to: 'media_links.media_id',
|
||||||
},
|
},
|
||||||
to: 'media.id',
|
to: 'media.id',
|
||||||
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('model_name', 'ManualJournal');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model defined fields.
|
||||||
|
*/
|
||||||
|
static get fields() {
|
||||||
|
return {
|
||||||
|
date: {
|
||||||
|
column: 'date',
|
||||||
|
},
|
||||||
|
journal_number: {
|
||||||
|
column: 'journal_number',
|
||||||
|
},
|
||||||
|
reference: {
|
||||||
|
column: 'reference',
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
column: 'status',
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
column: 'amount',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
column: 'description',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
column: 'user_id',
|
||||||
|
relation: 'users.id',
|
||||||
|
relationColumn: 'users.id',
|
||||||
|
},
|
||||||
|
journal_type: {
|
||||||
|
column: 'journal_type',
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
column: 'created_at',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
import TenantModel from 'models/TenantModel';
|
import TenantModel from 'models/TenantModel';
|
||||||
|
|
||||||
export default class Media extends TenantModel {
|
export default class Media extends TenantModel {
|
||||||
@@ -7,4 +8,29 @@ export default class Media extends TenantModel {
|
|||||||
static get tableName() {
|
static get tableName() {
|
||||||
return 'media';
|
return 'media';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model timestamps.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['createdAt', 'updatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const MediaLink = require('models/MediaLink');
|
||||||
|
|
||||||
|
return {
|
||||||
|
links: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: MediaLink.default,
|
||||||
|
join: {
|
||||||
|
from: 'media.id',
|
||||||
|
to: 'media_links.media_id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,17 +22,20 @@ export default class PaymentReceive extends TenantModel {
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const PaymentReceiveEntry = require('models/PaymentReceiveEntry');
|
const PaymentReceiveEntry = require('models/PaymentReceiveEntry');
|
||||||
const AccountTransaction = require('models/AccountTransaction');
|
const AccountTransaction = require('models/AccountTransaction');
|
||||||
const Customer = require('models/Customer');
|
const Contact = require('models/Contact');
|
||||||
const Account = require('models/Account');
|
const Account = require('models/Account');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
customer: {
|
customer: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Customer.default,
|
modelClass: Contact.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'payment_receives.customerId',
|
from: 'payment_receives.customerId',
|
||||||
to: 'customers.id',
|
to: 'contacts.id',
|
||||||
},
|
},
|
||||||
|
filter(query) {
|
||||||
|
query.where('contact_type', 'Customer');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
depositAccount: {
|
depositAccount: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user