Compare commits
49 Commits
contributi
...
docker-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1ea5c402c | ||
|
|
34b2c2c8b4 | ||
|
|
5d96fe6aa0 | ||
|
|
d2b5084b42 | ||
|
|
81fb0734d5 | ||
|
|
3639ce44e5 | ||
|
|
a7c00d60d5 | ||
|
|
932750b62d | ||
|
|
c90ffed67f | ||
|
|
e92c4486aa | ||
|
|
aaceea5338 | ||
|
|
4d54d180bc | ||
|
|
8fdd98e34d | ||
|
|
d53c5ee5e6 | ||
|
|
4082e4e2b8 | ||
|
|
0c689459cb | ||
|
|
40ef02f215 | ||
|
|
d369f0bb17 | ||
|
|
425d0293cc | ||
|
|
b621650975 | ||
|
|
40948160fe | ||
|
|
aa6b9dd295 | ||
|
|
05c2232b97 | ||
|
|
8f6325d529 | ||
|
|
0aa681043d | ||
|
|
40bddfdfeb | ||
|
|
d6e2f01d70 | ||
|
|
2344d3d34d | ||
|
|
883c5dcb41 | ||
|
|
be10b8934d | ||
|
|
ce38c71fa7 | ||
|
|
1162fbc7c3 | ||
|
|
18b9e25f2b | ||
|
|
dd26bdc482 | ||
|
|
ad3c9ebfe9 | ||
|
|
36611652da | ||
|
|
06c7ee71b4 | ||
|
|
54d3188666 | ||
|
|
3ceb9adda2 | ||
|
|
1249415054 | ||
|
|
4d44ce4c7f | ||
|
|
6c96c371c5 | ||
|
|
6c61a69f10 | ||
|
|
981b65349d | ||
|
|
a7d29a31c8 | ||
|
|
c1d92b74f0 | ||
|
|
6f0f47f38a | ||
|
|
83510cfa70 | ||
|
|
903dc0522a |
@@ -29,6 +29,15 @@ JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
|
|||||||
BASE_URL=https://bigcapital.ly
|
BASE_URL=https://bigcapital.ly
|
||||||
CONTACT_US_MAIL=support@bigcapital.ly
|
CONTACT_US_MAIL=support@bigcapital.ly
|
||||||
|
|
||||||
|
# App proxy
|
||||||
|
PUBLIC_PROXY_PORT=80
|
||||||
|
PUBLIC_PROXY_SSL_PORT=443
|
||||||
|
|
||||||
# Agendash
|
# Agendash
|
||||||
AGENDASH_AUTH_USER=agendash
|
AGENDASH_AUTH_USER=agendash
|
||||||
AGENDASH_AUTH_PASSWORD=123123
|
AGENDASH_AUTH_PASSWORD=123123
|
||||||
|
|
||||||
|
# Sign-up restrictions
|
||||||
|
SIGNUP_DISABLED=true
|
||||||
|
SIGNUP_ALLOWED_DOMAINS=
|
||||||
|
SIGNUP_ALLOWED_EMAILS=
|
||||||
18
CHANGELOG.md
@@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
All notable changes to Bigcapital server-side will be in this file.
|
All notable changes to Bigcapital server-side will be in this file.
|
||||||
|
|
||||||
|
## [0.9.1] - 28-05-2023
|
||||||
|
|
||||||
|
`@bigcapital/server`
|
||||||
|
- fix: deleting ledger entries of manual journal.
|
||||||
|
- fix: base currency should be enabled.
|
||||||
|
- fix: delete invoice transaction issue.
|
||||||
|
|
||||||
|
`@bigcapital/webapp`
|
||||||
|
- fix: general, accoutant and items preferences.
|
||||||
|
- fix: auto-increment sale invoices, estiamtes, credit notes, payments and manual journals.
|
||||||
|
- refactor: the setup organization form to use binded Formik components.
|
||||||
|
|
||||||
|
## [0.9.0] - 06-05-2023
|
||||||
|
|
||||||
|
`@bigcapital/server`
|
||||||
|
|
||||||
|
- [Sign-up restrictions](https://docs.bigcapital.ly/docs/deployment/signup_restriction) for self-hosting instances to disable signup or control the allowed email addresses and domains that can sign-up.
|
||||||
|
|
||||||
## [0.8.3] - 06-04-2023
|
## [0.8.3] - 06-04-2023
|
||||||
|
|
||||||
`@bigcaptial/monorepo`
|
`@bigcaptial/monorepo`
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Contributions via pull requests are much appreciated. Once the approach is agree
|
|||||||
## Contribute to Backend
|
## Contribute to Backend
|
||||||
|
|
||||||
- Clone the `bigcapital` repository and `cd` into `bigcapital` directory.
|
- Clone the `bigcapital` repository and `cd` into `bigcapital` directory.
|
||||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit the command on root directory and it will install dependencies of all packages.
|
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit these command on root directory and it will install dependencies of all packages.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
@@ -47,7 +47,7 @@ npm run bootstrap
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Wait some seconds, and hit `docker-compose ps` to see the result and you should see the same result below.
|
Wait some seconds, and hit `docker-compose ps` and you should see the same result below.
|
||||||
|
|
||||||
```
|
```
|
||||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
@@ -122,7 +122,7 @@ There are many other ways to get involved with the community and to participate
|
|||||||
- Use the product, submitting GitHub issues when a problem is found.
|
- Use the product, submitting GitHub issues when a problem is found.
|
||||||
- Help code review pull requests and participate in issue threads.
|
- Help code review pull requests and participate in issue threads.
|
||||||
- Submit a new feature request as an issue.
|
- Submit a new feature request as an issue.
|
||||||
- Help answer questions on forums such as Stack Overflow and SigNoz Community Slack Channel.
|
- Help answer questions on forums such as Bigcapital Community Discord Channel.
|
||||||
- Tell others about the project on Twitter, your blog, etc.
|
- Tell others about the project on Twitter, your blog, etc.
|
||||||
|
|
||||||
**[`^top^`](#)**
|
**[`^top^`](#)**
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
|
|||||||
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
|
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
|
||||||
- [Source Code](https://github.com/bigcapitalhq/bigcapital) - Github repo.
|
- [Source Code](https://github.com/bigcapitalhq/bigcapital) - Github repo.
|
||||||
|
|
||||||
# Changlog
|
# Changelog
|
||||||
|
|
||||||
Please see [Releases](https://github.com/bigcapitalhq/bigcapital/releases) for more information what has changed recently.
|
Please see [Releases](https://github.com/bigcapitalhq/bigcapital/releases) for more information what has changed recently.
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ services:
|
|||||||
- ./data/logs/nginx/:/var/log/nginx
|
- ./data/logs/nginx/:/var/log/nginx
|
||||||
- ./docker/certbot/certs/:/var/certs
|
- ./docker/certbot/certs/:/var/certs
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "${PUBLIC_PROXY_PORT:-80}:80"
|
||||||
- "443:443"
|
- "${PUBLIC_PROXY_SSL_PORT:-443}:443"
|
||||||
tty: true
|
tty: true
|
||||||
depends_on:
|
depends_on:
|
||||||
- server
|
- server
|
||||||
- webapp
|
- webapp
|
||||||
|
|
||||||
webapp:
|
webapp:
|
||||||
container_name: bigcapital-webapp
|
container_name: bigcapital-webapp
|
||||||
image: ghcr.io/bigcapitalhq/webapp:latest
|
image: ghcr.io/bigcapitalhq/webapp:latest
|
||||||
|
|
||||||
@@ -72,6 +72,11 @@ services:
|
|||||||
- AGENDASH_AUTH_USER=${AGENDASH_AUTH_USER}
|
- AGENDASH_AUTH_USER=${AGENDASH_AUTH_USER}
|
||||||
- AGENDASH_AUTH_PASSWORD=${AGENDASH_AUTH_PASSWORD}
|
- AGENDASH_AUTH_PASSWORD=${AGENDASH_AUTH_PASSWORD}
|
||||||
|
|
||||||
|
# Sign-up restrictions
|
||||||
|
- SIGNUP_DISABLED=${SIGNUP_DISABLED}
|
||||||
|
- SIGNUP_ALLOWED_DOMAINS=${SIGNUP_ALLOWED_DOMAINS}
|
||||||
|
- SIGNUP_ALLOWED_EMAILS=${SIGNUP_ALLOWED_EMAILS}
|
||||||
|
|
||||||
database_migration:
|
database_migration:
|
||||||
container_name: bigcapital-database-migration
|
container_name: bigcapital-database-migration
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ ARG MAIL_HOST= \
|
|||||||
BASE_URL= \
|
BASE_URL= \
|
||||||
# Agendash
|
# Agendash
|
||||||
AGENDASH_AUTH_USER=agendash \
|
AGENDASH_AUTH_USER=agendash \
|
||||||
AGENDASH_AUTH_PASSWORD=123123
|
AGENDASH_AUTH_PASSWORD=123123 \
|
||||||
|
# Sign-up restriction
|
||||||
|
SIGNUP_DISABLED= \
|
||||||
|
SIGNUP_ALLOWED_DOMAINS= \
|
||||||
|
SIGNUP_ALLOWED_EMAILS=
|
||||||
|
|
||||||
ENV MAIL_HOST=$MAIL_HOST \
|
ENV MAIL_HOST=$MAIL_HOST \
|
||||||
MAIL_USERNAME=$MAIL_USERNAME \
|
MAIL_USERNAME=$MAIL_USERNAME \
|
||||||
@@ -68,7 +72,11 @@ ENV MAIL_HOST=$MAIL_HOST \
|
|||||||
# MongoDB
|
# MongoDB
|
||||||
MONGODB_DATABASE_URL=$MONGODB_DATABASE_URL \
|
MONGODB_DATABASE_URL=$MONGODB_DATABASE_URL \
|
||||||
# Application
|
# Application
|
||||||
BASE_URL=$BASE_URL
|
BASE_URL=$BASE_URL \
|
||||||
|
# Sign-up restriction
|
||||||
|
SIGNUP_DISABLED=$SIGNUP_DISABLED \
|
||||||
|
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
||||||
|
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
||||||
|
|
||||||
# Create app directory.
|
# Create app directory.
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import { check, param, query } from 'express-validator';
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
import BaseController from '@/api/controllers/BaseController';
|
||||||
import { AbilitySubject, AccountAction, IAccountDTO } from '@/interfaces';
|
import {
|
||||||
|
AbilitySubject,
|
||||||
|
AccountAction,
|
||||||
|
IAccountDTO,
|
||||||
|
IAccountsStructureType,
|
||||||
|
} from '@/interfaces';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||||
@@ -172,6 +177,11 @@ export default class AccountsController extends BaseController {
|
|||||||
|
|
||||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||||
|
|
||||||
|
query('structure')
|
||||||
|
.optional()
|
||||||
|
.isString()
|
||||||
|
.isIn([IAccountsStructureType.Tree, IAccountsStructureType.Flat]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,6 +351,7 @@ export default class AccountsController extends BaseController {
|
|||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
columnSortBy: 'created_at',
|
columnSortBy: 'created_at',
|
||||||
inactiveMode: false,
|
inactiveMode: false,
|
||||||
|
structure: IAccountsStructureType.Tree,
|
||||||
...this.matchedQueryData(req),
|
...this.matchedQueryData(req),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export default class AuthenticationController extends BaseController {
|
|||||||
asyncMiddleware(this.resetPassword.bind(this)),
|
asyncMiddleware(this.resetPassword.bind(this)),
|
||||||
this.handlerErrors
|
this.handlerErrors
|
||||||
);
|
);
|
||||||
|
router.get('/meta', asyncMiddleware(this.getAuthMeta.bind(this)));
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +208,23 @@ export default class AuthenticationController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the authentication meta for SPA.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {Function} next
|
||||||
|
* @returns {Response|void}
|
||||||
|
*/
|
||||||
|
private async getAuthMeta(req: Request, res: Response, next: Function) {
|
||||||
|
try {
|
||||||
|
const meta = await this.authApplication.getAuthMeta();
|
||||||
|
|
||||||
|
return res.status(200).send({ meta });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the service errors.
|
* Handles the service errors.
|
||||||
*/
|
*/
|
||||||
@@ -247,6 +265,30 @@ export default class AuthenticationController extends BaseController {
|
|||||||
errors: [{ type: 'EMAIL.EXISTS', code: 600 }],
|
errors: [{ type: 'EMAIL.EXISTS', code: 600 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'SIGNUP_RESTRICTED') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
type: 'SIGNUP_RESTRICTED',
|
||||||
|
message:
|
||||||
|
'Sign-up is restricted no one can sign-up to the system.',
|
||||||
|
code: 700,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'SIGNUP_RESTRICTED_NOT_ALLOWED') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
type: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||||
|
message:
|
||||||
|
'Sign-up is restricted the given email address is not allowed to sign-up.',
|
||||||
|
code: 710,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
|
|||||||
get balanceSheetValidationSchema(): ValidationChain[] {
|
get balanceSheetValidationSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
...this.sheetNumberFormatValidationSchema,
|
...this.sheetNumberFormatValidationSchema,
|
||||||
query('accounting_method').optional().isIn(['cash', 'accural']),
|
query('accounting_method').optional().isIn(['cash', 'accrual']),
|
||||||
|
|
||||||
query('from_date').optional(),
|
query('from_date').optional(),
|
||||||
query('to_date').optional(),
|
query('to_date').optional(),
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export default class GeneralLedgerReportController extends BaseFinancialReportCo
|
|||||||
try {
|
try {
|
||||||
const { data, query, meta } =
|
const { data, query, meta } =
|
||||||
await this.generalLedgetService.generalLedger(tenantId, filter);
|
await this.generalLedgetService.generalLedger(tenantId, filter);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
meta: this.transfromToResponse(meta),
|
meta: this.transfromToResponse(meta),
|
||||||
data: this.transfromToResponse(data),
|
data: this.transfromToResponse(data),
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default class OrganizationController extends BaseController {
|
|||||||
private get organizationValidationSchema(): ValidationChain[] {
|
private get organizationValidationSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
check('name').exists().trim(),
|
check('name').exists().trim(),
|
||||||
check('industry').optional().isString(),
|
check('industry').optional({ nullable: true }).isString().trim().escape(),
|
||||||
check('location').exists().isString().isISO31661Alpha2(),
|
check('location').exists().isString().isISO31661Alpha2(),
|
||||||
check('base_currency').exists().isISO4217(),
|
check('base_currency').exists().isISO4217(),
|
||||||
check('timezone').exists().isIn(moment.tz.names()),
|
check('timezone').exists().isIn(moment.tz.names()),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import moment from 'moment';
|
|||||||
global.__root_dir = path.join(__dirname, '..');
|
global.__root_dir = path.join(__dirname, '..');
|
||||||
global.__resources_dir = path.join(global.__root_dir, 'resources');
|
global.__resources_dir = path.join(global.__root_dir, 'resources');
|
||||||
global.__locales_dir = path.join(global.__resources_dir, 'locales');
|
global.__locales_dir = path.join(global.__resources_dir, 'locales');
|
||||||
|
global.__views_dir = path.join(global.__root_dir, 'views');
|
||||||
|
|
||||||
moment.prototype.toMySqlDateTime = function () {
|
moment.prototype.toMySqlDateTime = function () {
|
||||||
return this.format('YYYY-MM-DD HH:mm:ss');
|
return this.format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { castCommaListEnvVarToArray, parseBoolean } from '@/utils';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@@ -146,6 +147,19 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign-up restrictions
|
||||||
|
*/
|
||||||
|
signupRestrictions: {
|
||||||
|
disabled: parseBoolean<boolean>(process.env.SIGNUP_DISABLED, false),
|
||||||
|
allowedDomains: castCommaListEnvVarToArray(
|
||||||
|
process.env.SIGNUP_ALLOWED_DOMAINS
|
||||||
|
),
|
||||||
|
allowedEmails: castCommaListEnvVarToArray(
|
||||||
|
process.env.SIGNUP_ALLOWED_EMAILS
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puppeteer remote browserless connection.
|
* Puppeteer remote browserless connection.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import AccountsData from '../data/accounts';
|
|||||||
|
|
||||||
export default class SeedAccounts extends TenantSeeder {
|
export default class SeedAccounts extends TenantSeeder {
|
||||||
/**
|
/**
|
||||||
* Seeds initial accounts to the organization.
|
* Seeds initial accounts to the organization.
|
||||||
*/
|
*/
|
||||||
up(knex) {
|
up(knex) {
|
||||||
const data = AccountsData.map((account) => {
|
const data = AccountsData.map((account) => ({
|
||||||
return {
|
...account,
|
||||||
...account,
|
name: this.i18n.__(account.name),
|
||||||
name: this.i18n.__(account.name),
|
description: this.i18n.__(account.description),
|
||||||
description: this.i18n.__(account.description),
|
currencyCode: this.tenant.metadata.baseCurrency,
|
||||||
currencyCode: this.tenant.metadata.baseCurrency,
|
seededAt: new Date(),
|
||||||
};
|
})
|
||||||
});
|
);
|
||||||
return knex('accounts').then(async () => {
|
return knex('accounts').then(async () => {
|
||||||
// Inserts seed entries.
|
// Inserts seed entries.
|
||||||
return knex('accounts').insert(data);
|
return knex('accounts').insert(data);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default class SeedSettings extends TenantSeeder {
|
|||||||
up() {
|
up() {
|
||||||
const settings = [
|
const settings = [
|
||||||
// Orgnization settings.
|
// Orgnization settings.
|
||||||
{ group: 'organization', key: 'accounting_basis', value: 'accural' },
|
{ group: 'organization', key: 'accounting_basis', value: 'accrual' },
|
||||||
|
|
||||||
// Accounts settings.
|
// Accounts settings.
|
||||||
{ group: 'accounts', key: 'account_code_unique', value: true },
|
{ group: 'accounts', key: 'account_code_unique', value: true },
|
||||||
|
|||||||
@@ -79,9 +79,15 @@ export interface IAccountTransaction {
|
|||||||
}
|
}
|
||||||
export interface IAccountResponse extends IAccount {}
|
export interface IAccountResponse extends IAccount {}
|
||||||
|
|
||||||
|
export enum IAccountsStructureType {
|
||||||
|
Tree = 'tree',
|
||||||
|
Flat = 'flat',
|
||||||
|
}
|
||||||
|
|
||||||
export interface IAccountsFilter extends IDynamicListFilterDTO {
|
export interface IAccountsFilter extends IDynamicListFilterDTO {
|
||||||
stringifiedFilterRoles?: string;
|
stringifiedFilterRoles?: string;
|
||||||
onlyInactive: boolean;
|
onlyInactive: boolean;
|
||||||
|
structure?: IAccountsStructureType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAccountType {
|
export interface IAccountType {
|
||||||
|
|||||||
@@ -74,4 +74,8 @@ export interface IAuthSendingResetPassword {
|
|||||||
export interface IAuthSendedResetPassword {
|
export interface IAuthSendedResetPassword {
|
||||||
user: ISystemUser,
|
user: ISystemUser,
|
||||||
token: string;
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAuthGetMetaPOJO {
|
||||||
|
signupDisabled: boolean;
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ export interface IBalanceSheetQuery extends IFinancialSheetBranchesQuery {
|
|||||||
numberFormat: INumberFormatQuery;
|
numberFormat: INumberFormatQuery;
|
||||||
noneTransactions: boolean;
|
noneTransactions: boolean;
|
||||||
noneZero: boolean;
|
noneZero: boolean;
|
||||||
basis: 'cash' | 'accural';
|
basis: 'cash' | 'accrual';
|
||||||
accountIds: number[];
|
accountIds: number[];
|
||||||
|
|
||||||
percentageOfColumn: boolean;
|
percentageOfColumn: boolean;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export interface ITrialBalanceSheetQuery {
|
|||||||
fromDate: Date | string;
|
fromDate: Date | string;
|
||||||
toDate: Date | string;
|
toDate: Date | string;
|
||||||
numberFormat: INumberFormatQuery;
|
numberFormat: INumberFormatQuery;
|
||||||
basis: 'cash' | 'accural';
|
basis: 'cash' | 'accrual';
|
||||||
noneZero: boolean;
|
noneZero: boolean;
|
||||||
noneTransactions: boolean;
|
noneTransactions: boolean;
|
||||||
onlyActive: boolean;
|
onlyActive: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AnyObject } from '@casl/ability/dist/types/types';
|
import { AnyObject } from '@casl/ability/dist/types/types';
|
||||||
import { ITenant } from '@/interfaces';
|
import { ITenant } from '@/interfaces';
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
|
import { Tenant } from '@/system/models';
|
||||||
|
|
||||||
export interface ISystemUser extends Model {
|
export interface ISystemUser extends Model {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -54,20 +55,52 @@ export interface IUserInvite {
|
|||||||
|
|
||||||
export interface IInviteUserService {
|
export interface IInviteUserService {
|
||||||
acceptInvite(token: string, inviteUserInput: IInviteUserInput): Promise<void>;
|
acceptInvite(token: string, inviteUserInput: IInviteUserInput): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-send user invite.
|
||||||
|
* @param {number} tenantId -
|
||||||
|
* @param {string} email -
|
||||||
|
* @return {Promise<{ invite: IUserInvite }>}
|
||||||
|
*/
|
||||||
resendInvite(
|
resendInvite(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<{
|
): Promise<{
|
||||||
invite: IUserInvite;
|
user: ITenantUser;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends invite mail to the given email from the given tenant and user.
|
||||||
|
* @param {number} tenantId -
|
||||||
|
* @param {string} email -
|
||||||
|
* @param {IUser} authorizedUser -
|
||||||
|
* @return {Promise<IUserInvite>}
|
||||||
|
*/
|
||||||
sendInvite(
|
sendInvite(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
email: string,
|
sendInviteDTO: IUserSendInviteDTO,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<{
|
): Promise<{
|
||||||
invite: IUserInvite;
|
invitedUser: ITenantUser;
|
||||||
}>;
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAcceptInviteUserService {
|
||||||
|
/**
|
||||||
|
* Accept the received invite.
|
||||||
|
* @param {string} token
|
||||||
|
* @param {IInviteUserInput} inviteUserInput
|
||||||
|
* @throws {ServiceErrors}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
acceptInvite(token: string, inviteUserDTO: IInviteUserInput): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the given invite token.
|
||||||
|
* @param {string} token - the given token string.
|
||||||
|
* @throws {ServiceError}
|
||||||
|
*/
|
||||||
checkInvite(
|
checkInvite(
|
||||||
token: string
|
token: string
|
||||||
): Promise<{ inviteToken: IUserInvite; orgName: object }>;
|
): Promise<{ inviteToken: IUserInvite; orgName: object }>;
|
||||||
@@ -121,7 +154,7 @@ export interface IUserInvitedEventPayload {
|
|||||||
tenantId: number;
|
tenantId: number;
|
||||||
user: ITenantUser;
|
user: ITenantUser;
|
||||||
}
|
}
|
||||||
export interface IUserInviteTenantSyncedEventPayload{
|
export interface IUserInviteTenantSyncedEventPayload {
|
||||||
invite: IUserInvite;
|
invite: IUserInvite;
|
||||||
authorizedUser: ISystemUser;
|
authorizedUser: ISystemUser;
|
||||||
tenantId: number;
|
tenantId: number;
|
||||||
@@ -143,10 +176,10 @@ export interface IAcceptInviteEventPayload {
|
|||||||
|
|
||||||
export interface ICheckInviteEventPayload {
|
export interface ICheckInviteEventPayload {
|
||||||
inviteToken: IUserInvite;
|
inviteToken: IUserInvite;
|
||||||
tenant: ITenant
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserSendInviteDTO {
|
export interface IUserSendInviteDTO {
|
||||||
email: string;
|
email: string;
|
||||||
roleId: number;
|
roleId: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Container, Inject } from 'typedi';
|
import { Container, Inject } from 'typedi';
|
||||||
import InviteUserService from '@/services/InviteUsers/AcceptInviteUser';
|
import InviteUserService from '@/services/InviteUsers/AcceptInviteUser';
|
||||||
|
import SendInviteUsersMailMessage from '@/services/InviteUsers/SendInviteUsersMailMessage';
|
||||||
|
|
||||||
export default class UserInviteMailJob {
|
export default class UserInviteMailJob {
|
||||||
/**
|
/**
|
||||||
@@ -21,24 +22,17 @@ export default class UserInviteMailJob {
|
|||||||
*/
|
*/
|
||||||
public async handler(job, done: Function): Promise<void> {
|
public async handler(job, done: Function): Promise<void> {
|
||||||
const { invite, authorizedUser, tenantId } = job.attrs.data;
|
const { invite, authorizedUser, tenantId } = job.attrs.data;
|
||||||
|
const sendInviteMailMessage = Container.get(SendInviteUsersMailMessage);
|
||||||
const Logger = Container.get('logger');
|
|
||||||
const inviteUsersService = Container.get(InviteUserService);
|
|
||||||
|
|
||||||
Logger.info(`Send invite user mail - started: ${job.attrs.data}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await inviteUsersService.mailMessages.sendInviteMail(
|
await sendInviteMailMessage.sendInviteMail(
|
||||||
tenantId,
|
tenantId,
|
||||||
authorizedUser,
|
authorizedUser,
|
||||||
invite
|
invite
|
||||||
);
|
);
|
||||||
Logger.info(`Send invite user mail - finished: ${job.attrs.data}`);
|
|
||||||
done();
|
done();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.info(
|
console.log(error);
|
||||||
`Send invite user mail - error: ${job.attrs.data}, error: ${error}`
|
|
||||||
);
|
|
||||||
done(error);
|
done(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default class Mail {
|
|||||||
* Retrieve view content from the view directory.
|
* Retrieve view content from the view directory.
|
||||||
*/
|
*/
|
||||||
private getViewContent(): string {
|
private getViewContent(): string {
|
||||||
const filePath = path.join(global.__root_dir, `../views/${this.view}`);
|
const filePath = path.join(global.__views_dir, `/${this.view}`);
|
||||||
return fs.readFileSync(filePath, 'utf8');
|
return fs.readFileSync(filePath, 'utf8');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import moment from 'moment';
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
|
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
|
||||||
import { formatNumber } from 'utils';
|
import { formatNumber } from 'utils';
|
||||||
|
import { isArrayLikeObject } from 'lodash/fp';
|
||||||
|
|
||||||
export class Transformer {
|
export class Transformer {
|
||||||
public context: any;
|
public context: any;
|
||||||
@@ -39,12 +40,33 @@ export class Transformer {
|
|||||||
return object;
|
return object;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected preCollectionTransform = (object: any) => {
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected postCollectionTransform = (object: any) => {
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public work = (object: any) => {
|
public work = (object: any) => {
|
||||||
if (Array.isArray(object)) {
|
if (Array.isArray(object)) {
|
||||||
return object.map(this.getTransformation);
|
const preTransformed = this.preCollectionTransform(object);
|
||||||
|
const transformed = preTransformed.map(this.getTransformation);
|
||||||
|
|
||||||
|
return this.postCollectionTransform(transformed);
|
||||||
} else if (isObject(object)) {
|
} else if (isObject(object)) {
|
||||||
return this.getTransformation(object);
|
return this.getTransformation(object);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import SaleInvoiceAutoIncrementSubscriber from '@/subscribers/SaleInvoices/AutoI
|
|||||||
import SaleInvoiceConvertFromEstimateSubscriber from '@/subscribers/SaleInvoices/ConvertFromEstimate';
|
import SaleInvoiceConvertFromEstimateSubscriber from '@/subscribers/SaleInvoices/ConvertFromEstimate';
|
||||||
import PaymentReceiveAutoSerialSubscriber from '@/subscribers/PaymentReceive/AutoSerialIncrement';
|
import PaymentReceiveAutoSerialSubscriber from '@/subscribers/PaymentReceive/AutoSerialIncrement';
|
||||||
import SyncSystemSendInvite from '@/services/InviteUsers/SyncSystemSendInvite';
|
import SyncSystemSendInvite from '@/services/InviteUsers/SyncSystemSendInvite';
|
||||||
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotification';
|
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotificationSubscribe';
|
||||||
import SyncTenantAcceptInvite from '@/services/InviteUsers/SyncTenantAcceptInvite';
|
import SyncTenantAcceptInvite from '@/services/InviteUsers/SyncTenantAcceptInvite';
|
||||||
import SyncTenantUserMutate from '@/services/Users/SyncTenantUserSaved';
|
import SyncTenantUserMutate from '@/services/Users/SyncTenantUserSaved';
|
||||||
import { SyncTenantUserDelete } from '@/services/Users/SyncTenantUserDeleted';
|
import { SyncTenantUserDelete } from '@/services/Users/SyncTenantUserDeleted';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class LedgerRevert {
|
|||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
ledgerStorage: LedgerStorageService;
|
private ledgerStorage: LedgerStorageService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts the jouranl entries.
|
* Reverts the jouranl entries.
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { IAccount } from '@/interfaces';
|
import { IAccount, IAccountsStructureType } from '@/interfaces';
|
||||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
import { formatNumber } from 'utils';
|
import {
|
||||||
|
assocDepthLevelToObjectTree,
|
||||||
|
flatToNestedArray,
|
||||||
|
formatNumber,
|
||||||
|
nestedArrayToFlatten,
|
||||||
|
} from 'utils';
|
||||||
|
|
||||||
export class AccountTransformer extends Transformer {
|
export class AccountTransformer extends Transformer {
|
||||||
/**
|
/**
|
||||||
@@ -8,7 +13,23 @@ export class AccountTransformer extends Transformer {
|
|||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
return ['formattedAmount'];
|
return ['formattedAmount', 'flattenName'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the flatten name with all dependants accounts names.
|
||||||
|
* @param {IAccount} account -
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public flattenName = (account: IAccount): string => {
|
||||||
|
const parentDependantsIds = this.options.accountsGraph.dependantsOf(
|
||||||
|
account.id
|
||||||
|
);
|
||||||
|
const prefixAccounts = parentDependantsIds.map((dependId) => {
|
||||||
|
const node = this.options.accountsGraph.getNodeData(dependId);
|
||||||
|
return `${node.name}: `;
|
||||||
|
});
|
||||||
|
return `${prefixAccounts}${account.name}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,8 +38,28 @@ export class AccountTransformer extends Transformer {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
protected formattedAmount = (account: IAccount): string => {
|
protected formattedAmount = (account: IAccount): string => {
|
||||||
return formatNumber(account.amount, {
|
return formatNumber(account.amount, { currencyCode: account.currencyCode });
|
||||||
currencyCode: account.currencyCode,
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the accounts collection to flat or nested array.
|
||||||
|
* @param {IAccount[]}
|
||||||
|
* @returns {IAccount[]}
|
||||||
|
*/
|
||||||
|
protected postCollectionTransform = (accounts: IAccount[]) => {
|
||||||
|
// Transfom the flatten to accounts tree.
|
||||||
|
const transformed = flatToNestedArray(accounts, {
|
||||||
|
id: 'id',
|
||||||
|
parentId: 'parentAccountId',
|
||||||
});
|
});
|
||||||
|
// Associate `accountLevel` attr to indicate object depth.
|
||||||
|
const transformed2 = assocDepthLevelToObjectTree(
|
||||||
|
transformed,
|
||||||
|
1,
|
||||||
|
'accountLevel'
|
||||||
|
);
|
||||||
|
return this.options.structure === IAccountsStructureType.Flat
|
||||||
|
? nestedArrayToFlatten(transformed2)
|
||||||
|
: transformed2;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,19 @@ export class GetAccount {
|
|||||||
*/
|
*/
|
||||||
public getAccount = async (tenantId: number, accountId: number) => {
|
public getAccount = async (tenantId: number, accountId: number) => {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Find the given account or throw not found error.
|
// Find the given account or throw not found error.
|
||||||
const account = await Account.query().findById(accountId).throwIfNotFound();
|
const account = await Account.query().findById(accountId).throwIfNotFound();
|
||||||
|
|
||||||
|
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||||
|
|
||||||
// Transformes the account model to POJO.
|
// Transformes the account model to POJO.
|
||||||
const transformed = await this.transformer.transform(
|
const transformed = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
account,
|
account,
|
||||||
new AccountTransformer()
|
new AccountTransformer(),
|
||||||
|
{ accountsGraph }
|
||||||
);
|
);
|
||||||
return this.i18nService.i18nApply(
|
return this.i18nService.i18nApply(
|
||||||
[['accountTypeLabel'], ['accountNormalFormatted']],
|
[['accountTypeLabel'], ['accountNormalFormatted']],
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { IAccountsFilter, IAccountResponse, IFilterMeta } from '@/interfaces';
|
import {
|
||||||
|
IAccountsFilter,
|
||||||
|
IAccountResponse,
|
||||||
|
IFilterMeta,
|
||||||
|
IAccountsStructureType,
|
||||||
|
} from '@/interfaces';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
import { AccountTransformer } from './AccountTransform';
|
import { AccountTransformer } from './AccountTransform';
|
||||||
@@ -38,6 +43,7 @@ export class GetAccounts {
|
|||||||
filterDTO: IAccountsFilter
|
filterDTO: IAccountsFilter
|
||||||
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> => {
|
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> => {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Parses the stringified filter roles.
|
// Parses the stringified filter roles.
|
||||||
const filter = this.parseListFilterDTO(filterDTO);
|
const filter = this.parseListFilterDTO(filterDTO);
|
||||||
@@ -53,17 +59,16 @@ export class GetAccounts {
|
|||||||
dynamicList.buildQuery()(builder);
|
dynamicList.buildQuery()(builder);
|
||||||
builder.modify('inactiveMode', filter.inactiveMode);
|
builder.modify('inactiveMode', filter.inactiveMode);
|
||||||
});
|
});
|
||||||
// Retrievs the formatted accounts collection.
|
|
||||||
const preTransformedAccounts = await this.transformer.transform(
|
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||||
|
|
||||||
|
// Retrieves the transformed accounts collection.
|
||||||
|
const transformedAccounts = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
accounts,
|
accounts,
|
||||||
new AccountTransformer()
|
new AccountTransformer(),
|
||||||
|
{ accountsGraph, structure: filterDTO.structure }
|
||||||
);
|
);
|
||||||
// Transform accounts to nested array.
|
|
||||||
const transformedAccounts = flatToNestedArray(preTransformedAccounts, {
|
|
||||||
id: 'id',
|
|
||||||
parentId: 'parentAccountId',
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts: transformedAccounts,
|
accounts: transformedAccounts,
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { Service, Inject, Container } from 'typedi';
|
import { Service, Inject, Container } from 'typedi';
|
||||||
import { IRegisterDTO, ISystemUser, IPasswordReset } from '@/interfaces';
|
import {
|
||||||
|
IRegisterDTO,
|
||||||
|
ISystemUser,
|
||||||
|
IPasswordReset,
|
||||||
|
IAuthGetMetaPOJO,
|
||||||
|
} from '@/interfaces';
|
||||||
import { AuthSigninService } from './AuthSignin';
|
import { AuthSigninService } from './AuthSignin';
|
||||||
import { AuthSignupService } from './AuthSignup';
|
import { AuthSignupService } from './AuthSignup';
|
||||||
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
||||||
|
import { GetAuthMeta } from './GetAuthMeta';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationApplication {
|
export default class AuthenticationApplication {
|
||||||
@@ -15,6 +21,9 @@ export default class AuthenticationApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private authResetPasswordService: AuthSendResetPassword;
|
private authResetPasswordService: AuthSendResetPassword;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private authGetMeta: GetAuthMeta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signin and generates JWT token.
|
* Signin and generates JWT token.
|
||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
@@ -53,4 +62,12 @@ export default class AuthenticationApplication {
|
|||||||
public async resetPassword(token: string, password: string): Promise<void> {
|
public async resetPassword(token: string, password: string): Promise<void> {
|
||||||
return this.authResetPasswordService.resetPassword(token, password);
|
return this.authResetPasswordService.resetPassword(token, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the authentication meta for SPA.
|
||||||
|
* @returns {Promise<IAuthGetMetaPOJO>}
|
||||||
|
*/
|
||||||
|
public async getAuthMeta(): Promise<IAuthGetMetaPOJO> {
|
||||||
|
return this.authGetMeta.getAuthMeta();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omit } from 'lodash';
|
import { isEmpty, omit } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +13,7 @@ import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
|||||||
import TenantsManagerService from '../Tenancy/TenantsManager';
|
import TenantsManagerService from '../Tenancy/TenantsManager';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import { hashPassword } from '@/utils';
|
import { hashPassword } from '@/utils';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
export class AuthSignupService {
|
export class AuthSignupService {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -33,6 +34,9 @@ export class AuthSignupService {
|
|||||||
public async signUp(signupDTO: IRegisterDTO): Promise<ISystemUser> {
|
public async signUp(signupDTO: IRegisterDTO): Promise<ISystemUser> {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
|
|
||||||
|
// Validates the signup disable restrictions.
|
||||||
|
await this.validateSignupRestrictions(signupDTO.email);
|
||||||
|
|
||||||
// Validates the given email uniqiness.
|
// Validates the given email uniqiness.
|
||||||
await this.validateEmailUniqiness(signupDTO.email);
|
await this.validateEmailUniqiness(signupDTO.email);
|
||||||
|
|
||||||
@@ -74,4 +78,34 @@ export class AuthSignupService {
|
|||||||
throw new ServiceError(ERRORS.EMAIL_EXISTS);
|
throw new ServiceError(ERRORS.EMAIL_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate sign-up disable restrictions.
|
||||||
|
* @param {string} email
|
||||||
|
*/
|
||||||
|
private async validateSignupRestrictions(email: string) {
|
||||||
|
// Can't continue if the signup is not disabled.
|
||||||
|
if (!config.signupRestrictions.disabled) return;
|
||||||
|
|
||||||
|
// Validate the allowed email addresses and domains.
|
||||||
|
if (
|
||||||
|
!isEmpty(config.signupRestrictions.allowedEmails) ||
|
||||||
|
!isEmpty(config.signupRestrictions.allowedDomains)
|
||||||
|
) {
|
||||||
|
const emailDomain = email.split('@').pop();
|
||||||
|
const isAllowedEmail =
|
||||||
|
config.signupRestrictions.allowedEmails.indexOf(email) !== -1;
|
||||||
|
|
||||||
|
const isAllowedDomain = config.signupRestrictions.allowedDomains.some(
|
||||||
|
(domain) => emailDomain === domain
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAllowedEmail && !isAllowedDomain) {
|
||||||
|
throw new ServiceError(ERRORS.SIGNUP_RESTRICTED_NOT_ALLOWED);
|
||||||
|
}
|
||||||
|
// Throw error if the signup is disabled with no exceptions.
|
||||||
|
} else {
|
||||||
|
throw new ServiceError(ERRORS.SIGNUP_RESTRICTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
packages/server/src/services/Authentication/GetAuthMeta.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { IAuthGetMetaPOJO } from '@/interfaces';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class GetAuthMeta {
|
||||||
|
/**
|
||||||
|
* Retrieves the authentication meta for SPA.
|
||||||
|
* @returns {Promise<IAuthGetMetaPOJO>}
|
||||||
|
*/
|
||||||
|
public async getAuthMeta(): Promise<IAuthGetMetaPOJO> {
|
||||||
|
return {
|
||||||
|
signupDisabled: config.signupRestrictions.disabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,4 +7,6 @@ export const ERRORS = {
|
|||||||
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
||||||
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
||||||
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
||||||
|
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||||
|
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,18 +5,13 @@ import {
|
|||||||
ICreditNoteDeletedPayload,
|
ICreditNoteDeletedPayload,
|
||||||
ICreditNoteEditedPayload,
|
ICreditNoteEditedPayload,
|
||||||
ICreditNoteOpenedPayload,
|
ICreditNoteOpenedPayload,
|
||||||
IRefundCreditNoteOpenedPayload,
|
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import CreditNoteGLEntries from './CreditNoteGLEntries';
|
import CreditNoteGLEntries from './CreditNoteGLEntries';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class CreditNoteGLEntriesSubscriber {
|
export default class CreditNoteGLEntriesSubscriber {
|
||||||
@Inject()
|
@Inject()
|
||||||
creditNoteGLEntries: CreditNoteGLEntries;
|
private creditNoteGLEntries: CreditNoteGLEntries;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches events with handlers.
|
* Attaches events with handlers.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ICashflowAccountTransactionsQuery, IPaginationMeta } from '@/interfaces
|
|||||||
@Service()
|
@Service()
|
||||||
export default class CashflowAccountTransactionsRepo {
|
export default class CashflowAccountTransactionsRepo {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the cashflow account transactions.
|
* Retrieve the cashflow account transactions.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const getDefaultPLQuery = (): IProfitLossSheetQuery => ({
|
|||||||
formatMoney: 'total',
|
formatMoney: 'total',
|
||||||
precision: 2,
|
precision: 2,
|
||||||
},
|
},
|
||||||
basis: 'accural',
|
basis: 'accrual',
|
||||||
|
|
||||||
noneZero: false,
|
noneZero: false,
|
||||||
noneTransactions: false,
|
noneTransactions: false,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
|||||||
formatMoney: 'total',
|
formatMoney: 'total',
|
||||||
precision: 2,
|
precision: 2,
|
||||||
},
|
},
|
||||||
basis: 'accural',
|
basis: 'accrual',
|
||||||
noneZero: false,
|
noneZero: false,
|
||||||
noneTransactions: true,
|
noneTransactions: true,
|
||||||
onlyActive: false,
|
onlyActive: false,
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import {
|
|||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import { IAcceptInviteUserService } from '@/interfaces';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AcceptInviteUserService {
|
export default class AcceptInviteUserService
|
||||||
|
implements IAcceptInviteUserService
|
||||||
|
{
|
||||||
@Inject()
|
@Inject()
|
||||||
private eventPublisher: EventPublisher;
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { IUserInviteTenantSyncedEventPayload } from '@/interfaces';
|
||||||
IUserInvitedEventPayload,
|
|
||||||
IUserInviteTenantSyncedEventPayload,
|
|
||||||
} from '@/interfaces';
|
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
|
import path from 'path';
|
||||||
import { ISystemUser } from '@/interfaces';
|
import { ISystemUser } from '@/interfaces';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
import Mail from '@/lib/Mail';
|
import Mail from '@/lib/Mail';
|
||||||
import { Service, Container } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import config from '@/config';
|
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class InviteUsersMailMessages {
|
export default class SendInviteUsersMailMessage {
|
||||||
/**
|
/**
|
||||||
* Sends invite mail to the given email.
|
* Sends invite mail to the given email.
|
||||||
* @param user
|
* @param user
|
||||||
@@ -18,7 +18,7 @@ export default class InviteUsersMailMessages {
|
|||||||
.findById(tenantId)
|
.findById(tenantId)
|
||||||
.withGraphFetched('metadata');
|
.withGraphFetched('metadata');
|
||||||
|
|
||||||
const root = __dirname + '/../../../views/images/bigcapital.png';
|
const root = path.join(global.__views_dir, '/images/bigcapital.png');
|
||||||
|
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
||||||
@@ -8,7 +8,7 @@ import { IAcceptInviteEventPayload } from '@/interfaces';
|
|||||||
@Service()
|
@Service()
|
||||||
export default class SyncTenantAcceptInvite {
|
export default class SyncTenantAcceptInvite {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches events with handlers.
|
* Attaches events with handlers.
|
||||||
|
|||||||
@@ -74,17 +74,15 @@ export default class InviteTenantUserService implements IInviteUserService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-send user invite.
|
* Re-send user invite.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {string} email -
|
* @param {string} email -
|
||||||
* @return {Promise<{ invite: IUserInvite }>}
|
* @return {Promise<{ invite: IUserInvite }>}
|
||||||
*/
|
*/
|
||||||
public async resendInvite(
|
public async resendInvite(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<{
|
): Promise<{ user: ITenantUser }> {
|
||||||
user: ITenantUser;
|
|
||||||
}> {
|
|
||||||
// Retrieve the user by id or throw not found service error.
|
// Retrieve the user by id or throw not found service error.
|
||||||
const user = await this.getUserByIdOrThrowError(tenantId, userId);
|
const user = await this.getUserByIdOrThrowError(tenantId, userId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { difference, sumBy, omit, map } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import {
|
import {
|
||||||
IManualJournalDTO,
|
IManualJournalDTO,
|
||||||
IManualJournalEntry,
|
IManualJournalEntry,
|
||||||
IManualJournal,
|
IManualJournal,
|
||||||
IManualJournalEntryDTO,
|
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { ERRORS } from './constants';
|
import { ERRORS } from './constants';
|
||||||
@@ -286,7 +285,7 @@ export class CommandManualJournalValidators {
|
|||||||
public validateJournalCurrencyWithAccountsCurrency = async (
|
public validateJournalCurrencyWithAccountsCurrency = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
manualJournalDTO: IManualJournalDTO,
|
manualJournalDTO: IManualJournalDTO,
|
||||||
baseCurrency: string,
|
baseCurrency: string
|
||||||
) => {
|
) => {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
|||||||
@@ -3,25 +3,20 @@ import * as R from 'ramda';
|
|||||||
import {
|
import {
|
||||||
IManualJournal,
|
IManualJournal,
|
||||||
IManualJournalEntry,
|
IManualJournalEntry,
|
||||||
IAccount,
|
|
||||||
ILedgerEntry,
|
ILedgerEntry,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import Ledger from '@/services/Accounting/Ledger';
|
import Ledger from '@/services/Accounting/Ledger';
|
||||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { LedgerRevert } from '@/services/Accounting/LedgerStorageRevert';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ManualJournalGLEntries {
|
export class ManualJournalGLEntries {
|
||||||
@Inject()
|
@Inject()
|
||||||
ledgerStorage: LedgerStorageService;
|
private ledgerStorage: LedgerStorageService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
ledgerRevert: LedgerRevert;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create manual journal GL entries.
|
* Create manual journal GL entries.
|
||||||
@@ -77,7 +72,7 @@ export class ManualJournalGLEntries {
|
|||||||
manualJournalId: number,
|
manualJournalId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
return this.ledgerRevert.revertGLEntries(
|
return this.ledgerStorage.deleteByReference(
|
||||||
tenantId,
|
tenantId,
|
||||||
manualJournalId,
|
manualJournalId,
|
||||||
'Journal',
|
'Journal',
|
||||||
@@ -86,7 +81,7 @@ export class ManualJournalGLEntries {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieves the ledger of the given manual journal.
|
||||||
* @param {IManualJournal} manualJournal
|
* @param {IManualJournal} manualJournal
|
||||||
* @returns {Ledger}
|
* @returns {Ledger}
|
||||||
*/
|
*/
|
||||||
@@ -97,11 +92,13 @@ export class ManualJournalGLEntries {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieves the common entry details of the manual journal
|
||||||
* @param {IManualJournal} manualJournal
|
* @param {IManualJournal} manualJournal
|
||||||
* @returns {}
|
* @returns {Partial<ILedgerEntry>}
|
||||||
*/
|
*/
|
||||||
private getManualJournalCommonEntry = (manualJournal: IManualJournal) => {
|
private getManualJournalCommonEntry = (
|
||||||
|
manualJournal: IManualJournal
|
||||||
|
): Partial<ILedgerEntry> => {
|
||||||
return {
|
return {
|
||||||
transactionNumber: manualJournal.journalNumber,
|
transactionNumber: manualJournal.journalNumber,
|
||||||
referenceNumber: manualJournal.reference,
|
referenceNumber: manualJournal.reference,
|
||||||
@@ -118,7 +115,8 @@ export class ManualJournalGLEntries {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieves the ledger entry of the given manual journal and
|
||||||
|
* its associated entry.
|
||||||
* @param {IManualJournal} manualJournal -
|
* @param {IManualJournal} manualJournal -
|
||||||
* @param {IManualJournalEntry} entry -
|
* @param {IManualJournalEntry} entry -
|
||||||
* @returns {ILedgerEntry}
|
* @returns {ILedgerEntry}
|
||||||
@@ -149,7 +147,7 @@ export class ManualJournalGLEntries {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieves the ledger of the given manual journal.
|
||||||
* @param {IManualJournal} manualJournal
|
* @param {IManualJournal} manualJournal
|
||||||
* @returns {ILedgerEntry[]}
|
* @returns {ILedgerEntry[]}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ export class ProjectBillableBillSubscriber {
|
|||||||
events.saleInvoice.onCreated,
|
events.saleInvoice.onCreated,
|
||||||
this.handleIncreaseBillableBill
|
this.handleIncreaseBillableBill
|
||||||
);
|
);
|
||||||
bus.subscribe(events.saleInvoice.onEdited, this.handleDecreaseBillableBill);
|
bus.subscribe(events.saleInvoice.onEdited, this.handleEditBillableBill);
|
||||||
bus.subscribe(events.saleInvoice.onDeleted, this.handleEditBillableBill);
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onDeleted,
|
||||||
|
this.handleDecreaseBillableBill
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import async from 'async';
|
import async from 'async';
|
||||||
import { ISaleInvoice, ISaleInvoiceDTO, ProjectLinkRefType } from '@/interfaces';
|
import {
|
||||||
|
ISaleInvoice,
|
||||||
|
ISaleInvoiceDTO,
|
||||||
|
ProjectLinkRefType,
|
||||||
|
} from '@/interfaces';
|
||||||
import { ProjectBillableExpense } from './ProjectBillableExpense';
|
import { ProjectBillableExpense } from './ProjectBillableExpense';
|
||||||
import { filterEntriesByRefType } from './_utils';
|
import { filterEntriesByRefType } from './_utils';
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,10 @@ export class ProjectBillableExpensesSubscriber {
|
|||||||
events.saleInvoice.onCreated,
|
events.saleInvoice.onCreated,
|
||||||
this.handleIncreaseBillableExpenses
|
this.handleIncreaseBillableExpenses
|
||||||
);
|
);
|
||||||
bus.subscribe(
|
bus.subscribe(events.saleInvoice.onEdited, this.handleEditBillableExpenses);
|
||||||
events.saleInvoice.onEdited,
|
|
||||||
this.handleDecreaseBillableExpenses
|
|
||||||
);
|
|
||||||
bus.subscribe(
|
bus.subscribe(
|
||||||
events.saleInvoice.onDeleted,
|
events.saleInvoice.onDeleted,
|
||||||
this.handleEditBillableExpenses
|
this.handleDecreaseBillableExpenses
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,6 +419,58 @@ export const parseDate = (date: string) => {
|
|||||||
return date ? moment(date).utcOffset(0).format('YYYY-MM-DD') : '';
|
return date ? moment(date).utcOffset(0).format('YYYY-MM-DD') : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const nestedArrayToFlatten = (
|
||||||
|
collection,
|
||||||
|
property = 'children',
|
||||||
|
parseItem = (a, level) => a,
|
||||||
|
level = 1
|
||||||
|
) => {
|
||||||
|
const parseObject = (obj) =>
|
||||||
|
parseItem(
|
||||||
|
{
|
||||||
|
..._.omit(obj, [property]),
|
||||||
|
},
|
||||||
|
level
|
||||||
|
);
|
||||||
|
|
||||||
|
return collection.reduce((items, currentValue, index) => {
|
||||||
|
let localItems = [...items];
|
||||||
|
const parsedItem = parseObject(currentValue, level);
|
||||||
|
localItems.push(parsedItem);
|
||||||
|
|
||||||
|
if (Array.isArray(currentValue[property])) {
|
||||||
|
const flattenArray = nestedArrayToFlatten(
|
||||||
|
currentValue[property],
|
||||||
|
property,
|
||||||
|
parseItem,
|
||||||
|
level + 1
|
||||||
|
);
|
||||||
|
localItems = _.concat(localItems, flattenArray);
|
||||||
|
}
|
||||||
|
return localItems;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const assocDepthLevelToObjectTree = (
|
||||||
|
objects,
|
||||||
|
level = 1,
|
||||||
|
propertyName = 'level'
|
||||||
|
) => {
|
||||||
|
for (let i = 0; i < objects.length; i++) {
|
||||||
|
const object = objects[i];
|
||||||
|
object[propertyName] = level;
|
||||||
|
|
||||||
|
if (object.children) {
|
||||||
|
assocDepthLevelToObjectTree(object.children, level + 1, propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
const castCommaListEnvVarToArray = (envVar: string): Array<string> => {
|
||||||
|
return envVar ? envVar?.split(',')?.map(_.trim) : [];
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
templateRender,
|
templateRender,
|
||||||
accumSum,
|
accumSum,
|
||||||
@@ -449,4 +501,7 @@ export {
|
|||||||
dateRangeFromToCollection,
|
dateRangeFromToCollection,
|
||||||
transformToMapKeyValue,
|
transformToMapKeyValue,
|
||||||
mergeObjectsBykey,
|
mergeObjectsBykey,
|
||||||
|
nestedArrayToFlatten,
|
||||||
|
assocDepthLevelToObjectTree,
|
||||||
|
castCommaListEnvVarToArray
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ USER root
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
COPY package.json ./
|
COPY package*.json ./
|
||||||
COPY lerna.json ./
|
COPY lerna.json ./
|
||||||
|
|
||||||
COPY ./packages/webapp/package.json /app/packages/webapp/package.json
|
COPY ./packages/webapp/package*.json /app/packages/webapp/
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
RUN npm run bootstrap
|
RUN npm run bootstrap
|
||||||
|
|||||||
17
packages/webapp/package-lock.json
generated
@@ -1205,9 +1205,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@blueprintjs-formik/core": {
|
"@blueprintjs-formik/core": {
|
||||||
"version": "0.2.1",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.3.3.tgz",
|
||||||
"integrity": "sha512-YGJe+QorDGbkWDSUg6x69LYGN62Kgvb92Iz/voqmszVRKj4KcoPvd/7coF8Jmu+ZQE6LcwM/9ccB2i63L99ITA==",
|
"integrity": "sha512-ko7g54YSEcSq2K/GEpmiTG0foGLqe7DwgXGhkGxYEiHhLAUv8WvQmrFsm8e/KOW7n8mLGq0uaZVe2l8m3JTGGQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.keyby": "^4.6.0",
|
"lodash.keyby": "^4.6.0",
|
||||||
@@ -1227,9 +1227,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@blueprintjs-formik/select": {
|
"@blueprintjs-formik/select": {
|
||||||
"version": "0.1.5",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/select/-/select-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/select/-/select-0.2.3.tgz",
|
||||||
"integrity": "sha512-EqGbuoiS1VrWpzjd39uVhBAmfVobdpgqalGcpODyGA+XAYoft1UU12yzTzrEOwBZpQKiC12UQwekUPspYBsVKA==",
|
"integrity": "sha512-j/zkX0B9wgtoHgK6Z/rlowB7F7zemrAajBU+d3caCoEYMMqwAI0XA++GytqrIhv5fEGjkZ1hkxS9j8eqX8vtjA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.keyby": "^4.6.0",
|
"lodash.keyby": "^4.6.0",
|
||||||
@@ -7298,6 +7298,11 @@
|
|||||||
"locate-path": "^3.0.0"
|
"locate-path": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flat": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
|
||||||
|
},
|
||||||
"flat-cache": {
|
"flat-cache": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
"version": "1.7.1",
|
"version": "1.7.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blueprintjs-formik/core": "^0.2.1",
|
"@blueprintjs-formik/core": "^0.3.3",
|
||||||
"@blueprintjs-formik/datetime": "^0.3.4",
|
"@blueprintjs-formik/datetime": "^0.3.4",
|
||||||
"@blueprintjs-formik/select": "^0.1.4",
|
"@blueprintjs-formik/select": "^0.2.3",
|
||||||
"@blueprintjs/core": "^3.50.2",
|
"@blueprintjs/core": "^3.50.2",
|
||||||
"@blueprintjs/datetime": "^3.23.12",
|
"@blueprintjs/datetime": "^3.23.12",
|
||||||
"@blueprintjs/popover2": "^0.11.1",
|
"@blueprintjs/popover2": "^0.11.1",
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
"@casl/react": "^2.3.0",
|
"@casl/react": "^2.3.0",
|
||||||
"@craco/craco": "^5.9.0",
|
"@craco/craco": "^5.9.0",
|
||||||
"@reduxjs/toolkit": "^1.2.5",
|
"@reduxjs/toolkit": "^1.2.5",
|
||||||
"@sentry/react": "^6.13.2",
|
|
||||||
"@sentry/tracing": "^6.13.2",
|
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.4.0",
|
"@testing-library/react": "^9.4.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
@@ -45,6 +43,7 @@
|
|||||||
"deepdash": "^5.3.9",
|
"deepdash": "^5.3.9",
|
||||||
"dependency-graph": "^0.11.0",
|
"dependency-graph": "^0.11.0",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"flat": "^5.0.2",
|
||||||
"formik": "^2.2.5",
|
"formik": "^2.2.5",
|
||||||
"http-proxy-middleware": "^1.0.0",
|
"http-proxy-middleware": "^1.0.0",
|
||||||
"jest": "24.9.0",
|
"jest": "24.9.0",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |