mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital
This commit is contained in:
@@ -51,13 +51,6 @@ export default class ItemsController extends BaseController {
|
|||||||
asyncMiddleware(this.editItem.bind(this)),
|
asyncMiddleware(this.editItem.bind(this)),
|
||||||
this.handlerServiceErrors
|
this.handlerServiceErrors
|
||||||
);
|
);
|
||||||
router.delete(
|
|
||||||
'/',
|
|
||||||
[...this.validateBulkSelectSchema],
|
|
||||||
this.validationResult,
|
|
||||||
asyncMiddleware(this.bulkDeleteItems.bind(this)),
|
|
||||||
this.handlerServiceErrors
|
|
||||||
);
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
[...this.validateSpecificItemSchema],
|
[...this.validateSpecificItemSchema],
|
||||||
@@ -415,28 +408,6 @@ export default class ItemsController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes items in bulk.
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response} res
|
|
||||||
* @param {NextFunction} next
|
|
||||||
*/
|
|
||||||
async bulkDeleteItems(req: Request, res: Response, next: NextFunction) {
|
|
||||||
const { tenantId } = req;
|
|
||||||
const { ids: itemsIds } = req.query;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.itemsService.bulkDeleteItems(tenantId, itemsIds);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
ids: itemsIds,
|
|
||||||
message: 'Items have been deleted successfully.',
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles service errors.
|
* Handles service errors.
|
||||||
* @param {Error} error
|
* @param {Error} error
|
||||||
|
|||||||
@@ -1,32 +1,35 @@
|
|||||||
import { Service } from 'typedi';
|
import { Inject, 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';
|
||||||
import { IOptionDTO, IOptionsDTO } from 'interfaces';
|
import { IOptionDTO, IOptionsDTO } from 'interfaces';
|
||||||
import BaseController from 'api/controllers/BaseController';
|
import BaseController from 'api/controllers/BaseController';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import {
|
import { getDefinedOptions, isDefinedOptionConfigurable } from 'utils';
|
||||||
getDefinedOptions,
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
isDefinedOptionConfigurable,
|
|
||||||
} from 'utils';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SettingsController extends BaseController{
|
export default class SettingsController extends BaseController {
|
||||||
|
@Inject()
|
||||||
|
settingsService: SettingsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/',
|
router.post(
|
||||||
|
'/',
|
||||||
this.saveSettingsValidationSchema,
|
this.saveSettingsValidationSchema,
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.saveSettings.bind(this)),
|
asyncMiddleware(this.saveSettings.bind(this))
|
||||||
);
|
);
|
||||||
router.get('/',
|
router.get(
|
||||||
|
'/',
|
||||||
this.getSettingsSchema,
|
this.getSettingsSchema,
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getSettings.bind(this)),
|
asyncMiddleware(this.getSettings.bind(this))
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
@@ -46,7 +49,7 @@ export default class SettingsController extends BaseController{
|
|||||||
/**
|
/**
|
||||||
* Retrieve the application options from the storage.
|
* Retrieve the application options from the storage.
|
||||||
*/
|
*/
|
||||||
private get getSettingsSchema() {
|
private get getSettingsSchema() {
|
||||||
return [
|
return [
|
||||||
query('key').optional().trim().escape(),
|
query('key').optional().trim().escape(),
|
||||||
query('group').optional().trim().escape(),
|
query('group').optional().trim().escape(),
|
||||||
@@ -59,12 +62,15 @@ export default class SettingsController extends BaseController{
|
|||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
public async saveSettings(req: Request, res: Response, next) {
|
public async saveSettings(req: Request, res: Response, next) {
|
||||||
const { Option } = req.models;
|
const { tenantId } = req;
|
||||||
const optionsDTO: IOptionsDTO = this.matchedBodyData(req);
|
const optionsDTO: IOptionsDTO = this.matchedBodyData(req);
|
||||||
const { settings } = req;
|
const { settings } = req;
|
||||||
|
|
||||||
const errorReasons: { type: string, code: number, keys: [] }[] = [];
|
const errorReasons: { type: string; code: number; keys: [] }[] = [];
|
||||||
const notDefinedOptions = Option.validateDefined(optionsDTO.options);
|
const notDefinedOptions = this.settingsService.validateNotDefinedSettings(
|
||||||
|
tenantId,
|
||||||
|
optionsDTO.options
|
||||||
|
);
|
||||||
|
|
||||||
if (notDefinedOptions.length) {
|
if (notDefinedOptions.length) {
|
||||||
errorReasons.push({
|
errorReasons.push({
|
||||||
@@ -103,4 +109,4 @@ export default class SettingsController extends BaseController{
|
|||||||
|
|
||||||
return res.status(200).send({ settings: allSettings });
|
return res.status(200).send({ settings: allSettings });
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,166 +1,127 @@
|
|||||||
export default {
|
export default {
|
||||||
organization: [
|
organization: {
|
||||||
{
|
name: {
|
||||||
key: "name",
|
|
||||||
type: "string",
|
|
||||||
config: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "base_currency",
|
|
||||||
type: "string",
|
|
||||||
config: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "industry",
|
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
{
|
base_currency: {
|
||||||
key: "location",
|
type: 'string',
|
||||||
|
},
|
||||||
|
industry: {
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
{
|
location: {
|
||||||
key: "fiscal_year",
|
|
||||||
type: "string",
|
|
||||||
// config: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "financial_date_start",
|
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
{
|
fiscal_year: {
|
||||||
key: "language",
|
type: 'string',
|
||||||
type: "string",
|
|
||||||
config: true,
|
|
||||||
},
|
},
|
||||||
{
|
financial_date_start: {
|
||||||
key: "time_zone",
|
type: 'string',
|
||||||
type: "string",
|
|
||||||
},
|
},
|
||||||
{
|
language: {
|
||||||
key: "date_format",
|
type: 'string',
|
||||||
type: "string",
|
|
||||||
},
|
},
|
||||||
{
|
time_zone: {
|
||||||
key: 'accounting_basis',
|
type: 'string',
|
||||||
|
},
|
||||||
|
date_format: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
accounting_basis: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
manual_journals: [
|
manual_journals: {
|
||||||
{
|
next_number: {
|
||||||
key: "next_number",
|
type: 'string',
|
||||||
type: "string",
|
|
||||||
},
|
},
|
||||||
{
|
number_prefix: {
|
||||||
key: "number_prefix",
|
type: 'string',
|
||||||
type: "string",
|
|
||||||
},
|
},
|
||||||
{
|
auto_increment: {
|
||||||
key: "auto_increment",
|
type: 'boolean',
|
||||||
type: "boolean",
|
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
bill_payments: [
|
bill_payments: {
|
||||||
{
|
withdrawal_account: {
|
||||||
key: 'withdrawal_account',
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
sales_estimates: [
|
|
||||||
{
|
|
||||||
key: "next_number",
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "number_prefix",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "auto_increment",
|
|
||||||
type: "boolean",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
sales_receipts: [
|
|
||||||
{
|
|
||||||
key: "next_number",
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "number_prefix",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "auto_increment",
|
|
||||||
type: "boolean",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "preferred_deposit_account",
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sales_invoices: [
|
|
||||||
{
|
|
||||||
key: "next_number",
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "number_prefix",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "auto_increment",
|
|
||||||
type: "boolean",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
payment_receives: [
|
|
||||||
{
|
|
||||||
key: "next_number",
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "number_prefix",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "auto_increment",
|
|
||||||
type: "boolean",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'deposit_account',
|
|
||||||
type: 'string'
|
type: 'string'
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
key: 'advance_deposit',
|
sales_estimates: {
|
||||||
key: 'string'
|
next_number: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
number_prefix: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
auto_increment: {
|
||||||
|
type: "boolean",
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
items: [
|
sales_receipts: {
|
||||||
{
|
next_number: {
|
||||||
key: "sell_account",
|
type: "string",
|
||||||
type: "number",
|
|
||||||
},
|
},
|
||||||
{
|
number_prefix: {
|
||||||
key: "cost_account",
|
type: "string",
|
||||||
type: "number",
|
|
||||||
},
|
},
|
||||||
{
|
auto_increment: {
|
||||||
key: "inventory_account",
|
type: "boolean",
|
||||||
type: "number",
|
|
||||||
},
|
},
|
||||||
],
|
preferred_deposit_account: {
|
||||||
expenses: [
|
|
||||||
{
|
|
||||||
key: "preferred_payment_account",
|
|
||||||
type: "number",
|
type: "number",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sales_invoices: {
|
||||||
|
next_number: {
|
||||||
|
type: "string",
|
||||||
},
|
},
|
||||||
],
|
number_prefix: {
|
||||||
accounts: [
|
type: "string",
|
||||||
{
|
},
|
||||||
key: 'account_code_required',
|
auto_increment: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payment_receives: {
|
||||||
|
next_number: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
number_prefix: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
auto_increment: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
{
|
deposit_account: {
|
||||||
key: 'account_code_unique',
|
type: 'number',
|
||||||
|
},
|
||||||
|
advance_deposit: {
|
||||||
|
type: 'number',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
sell_account: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
cost_account: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
inventory_account: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
preferred_payment_account: {
|
||||||
|
type: "number",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
account_code_required: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
]
|
account_code_unique: {
|
||||||
|
type: 'boolean',
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,14 +61,10 @@ export interface IItemDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IItemsService {
|
export interface IItemsService {
|
||||||
bulkDeleteItems(tenantId: number, itemsIds: number[]): Promise<void>;
|
|
||||||
|
|
||||||
getItem(tenantId: number, itemId: number): Promise<IItem>;
|
getItem(tenantId: number, itemId: number): Promise<IItem>;
|
||||||
deleteItem(tenantId: number, itemId: number): Promise<void>;
|
deleteItem(tenantId: number, itemId: number): Promise<void>;
|
||||||
editItem(tenantId: number, itemId: number, itemDTO: IItemDTO): Promise<IItem>;
|
editItem(tenantId: number, itemId: number, itemDTO: IItemDTO): Promise<IItem>;
|
||||||
|
|
||||||
newItem(tenantId: number, itemDTO: IItemDTO): Promise<IItem>;
|
newItem(tenantId: number, itemDTO: IItemDTO): Promise<IItem>;
|
||||||
|
|
||||||
itemsList(tenantId: number, itemsFilter: IItemsFilter): Promise<{items: IItem[]}>;
|
itemsList(tenantId: number, itemsFilter: IItemsFilter): Promise<{items: IItem[]}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
server/src/lib/Metable/MetableConfig.ts
Normal file
40
server/src/lib/Metable/MetableConfig.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
|
export default class MetableConfig {
|
||||||
|
readonly config: any;
|
||||||
|
|
||||||
|
constructor(config) {
|
||||||
|
this.setConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets config.
|
||||||
|
*/
|
||||||
|
setConfig(config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} group
|
||||||
|
* @param {string} accessor
|
||||||
|
* @returns {object|string}
|
||||||
|
*/
|
||||||
|
getMetaConfig(key: string, group?: string, accessor?: string) {
|
||||||
|
const configGroup = get(this.config, group);
|
||||||
|
const config = get(configGroup, key);
|
||||||
|
|
||||||
|
return accessor ? get(config, accessor) : config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} group
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getMetaType(key: string, group?: string) {
|
||||||
|
return this.getMetaConfig(key, group, 'type');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Model } from 'objection';
|
import { IMetadata, IMetableStoreStorage } from 'interfaces';
|
||||||
import {
|
|
||||||
IMetadata,
|
|
||||||
IMetableStoreStorage,
|
|
||||||
} from 'interfaces';
|
|
||||||
import MetableStore from './MetableStore';
|
import MetableStore from './MetableStore';
|
||||||
import { isBlank } from 'utils';
|
import { isBlank, parseBoolean } from 'utils';
|
||||||
|
import MetableConfig from './MetableConfig';
|
||||||
export default class MetableDBStore extends MetableStore implements IMetableStoreStorage{
|
import config from 'data/options'
|
||||||
|
export default class MetableDBStore
|
||||||
|
extends MetableStore
|
||||||
|
implements IMetableStoreStorage {
|
||||||
repository: any;
|
repository: any;
|
||||||
KEY_COLUMN: string;
|
KEY_COLUMN: string;
|
||||||
VALUE_COLUMN: string;
|
VALUE_COLUMN: string;
|
||||||
TYPE_COLUMN: string;
|
TYPE_COLUMN: string;
|
||||||
extraQuery: Function;
|
extraQuery: Function;
|
||||||
loaded: Boolean;
|
loaded: Boolean;
|
||||||
|
config: MetableConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor method.
|
* Constructor method.
|
||||||
@@ -32,6 +32,7 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
...this.transfromMetaExtraColumns(meta),
|
...this.transfromMetaExtraColumns(meta),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
this.config = new MetableConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,17 +85,18 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
saveUpdated(metadata: IMetadata[]) {
|
saveUpdated(metadata: IMetadata[]) {
|
||||||
const updated = metadata.filter((m) => (m._markAsUpdated === true));
|
const updated = metadata.filter((m) => m._markAsUpdated === true);
|
||||||
const opers = [];
|
const opers = [];
|
||||||
|
|
||||||
updated.forEach((meta) => {
|
updated.forEach((meta) => {
|
||||||
const updateOper = this.repository.update({
|
const updateOper = this.repository
|
||||||
[this.VALUE_COLUMN]: meta.value,
|
.update(
|
||||||
}, {
|
{ [this.VALUE_COLUMN]: meta.value },
|
||||||
...this.extraQuery(meta),
|
{ ...this.extraQuery(meta) }
|
||||||
}).then(() => {
|
)
|
||||||
meta._markAsUpdated = false;
|
.then(() => {
|
||||||
});
|
meta._markAsUpdated = false;
|
||||||
|
});
|
||||||
opers.push(updateOper);
|
opers.push(updateOper);
|
||||||
});
|
});
|
||||||
return Promise.all(opers);
|
return Promise.all(opers);
|
||||||
@@ -106,16 +108,20 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
saveDeleted(metadata: IMetadata[]) {
|
saveDeleted(metadata: IMetadata[]) {
|
||||||
const deleted = metadata.filter((m: IMetadata) => (m._markAsDeleted === true));
|
const deleted = metadata.filter(
|
||||||
|
(m: IMetadata) => m._markAsDeleted === true
|
||||||
|
);
|
||||||
const opers: Promise<void> = [];
|
const opers: Promise<void> = [];
|
||||||
|
|
||||||
if (deleted.length > 0) {
|
if (deleted.length > 0) {
|
||||||
deleted.forEach((meta) => {
|
deleted.forEach((meta) => {
|
||||||
const deleteOper = this.repository.deleteBy({
|
const deleteOper = this.repository
|
||||||
...this.extraQuery(meta),
|
.deleteBy({
|
||||||
}).then(() => {
|
...this.extraQuery(meta),
|
||||||
meta._markAsDeleted = false;
|
})
|
||||||
});
|
.then(() => {
|
||||||
|
meta._markAsDeleted = false;
|
||||||
|
});
|
||||||
opers.push(deleteOper);
|
opers.push(deleteOper);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -128,7 +134,9 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
saveInserted(metadata: IMetadata[]) {
|
saveInserted(metadata: IMetadata[]) {
|
||||||
const inserted = metadata.filter((m: IMetadata) => (m._markAsInserted === true));
|
const inserted = metadata.filter(
|
||||||
|
(m: IMetadata) => m._markAsInserted === true
|
||||||
|
);
|
||||||
const opers: Promise<void> = [];
|
const opers: Promise<void> = [];
|
||||||
|
|
||||||
inserted.forEach((meta) => {
|
inserted.forEach((meta) => {
|
||||||
@@ -137,10 +145,9 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
[this.VALUE_COLUMN]: meta.value,
|
[this.VALUE_COLUMN]: meta.value,
|
||||||
...this.transfromMetaExtraColumns(meta),
|
...this.transfromMetaExtraColumns(meta),
|
||||||
};
|
};
|
||||||
const insertOper = this.repository.create(insertData)
|
const insertOper = this.repository.create(insertData).then(() => {
|
||||||
.then(() => {
|
meta._markAsInserted = false;
|
||||||
meta._markAsInserted = false;
|
});
|
||||||
});
|
|
||||||
opers.push(insertOper);
|
opers.push(insertOper);
|
||||||
});
|
});
|
||||||
return Promise.all(opers);
|
return Promise.all(opers);
|
||||||
@@ -164,20 +171,23 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the metadata before saving to the database.
|
* Parse the metadata values after fetching it from the storage.
|
||||||
* @param {String|Number|Boolean} value -
|
* @param {String|Number|Boolean} value -
|
||||||
* @param {String} valueType -
|
* @param {String} valueType -
|
||||||
* @return {String|Number|Boolean} -
|
* @return {String|Number|Boolean} -
|
||||||
*/
|
*/
|
||||||
static formatMetaValue(value: string|number|boolean, valueType: string|false) {
|
static parseMetaValue(
|
||||||
let parsedValue: string|number|boolean;
|
value: string,
|
||||||
|
valueType: string | false
|
||||||
|
): string | boolean | number {
|
||||||
|
let parsedValue: string | number | boolean;
|
||||||
|
|
||||||
switch (valueType) {
|
switch (valueType) {
|
||||||
case 'number':
|
case 'number':
|
||||||
parsedValue = `${value}`;
|
parsedValue = parseFloat(value);
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
parsedValue = value ? '1' : '0';
|
parsedValue = parseBoolean(value, false);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
parsedValue = JSON.stringify(parsedValue);
|
parsedValue = JSON.stringify(parsedValue);
|
||||||
@@ -195,11 +205,15 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
* @param {String} parseType -
|
* @param {String} parseType -
|
||||||
*/
|
*/
|
||||||
mapMetadata(metadata: IMetadata) {
|
mapMetadata(metadata: IMetadata) {
|
||||||
|
const metaType = this.config.getMetaType(
|
||||||
|
metadata[this.KEY_COLUMN],
|
||||||
|
metadata['group'],
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
key: metadata[this.KEY_COLUMN],
|
key: metadata[this.KEY_COLUMN],
|
||||||
value: MetableDBStore.formatMetaValue(
|
value: MetableDBStore.parseMetaValue(
|
||||||
metadata[this.VALUE_COLUMN],
|
metadata[this.VALUE_COLUMN],
|
||||||
this.TYPE_COLUMN ? metadata[this.TYPE_COLUMN] : false,
|
metaType
|
||||||
),
|
),
|
||||||
...this.extraColumns.reduce((obj, extraCol: string) => {
|
...this.extraColumns.reduce((obj, extraCol: string) => {
|
||||||
obj[extraCol] = metadata[extraCol] || null;
|
obj[extraCol] = metadata[extraCol] || null;
|
||||||
@@ -220,8 +234,10 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
|||||||
* Throw error in case the store is not loaded yet.
|
* Throw error in case the store is not loaded yet.
|
||||||
*/
|
*/
|
||||||
private validateStoreIsLoaded() {
|
private validateStoreIsLoaded() {
|
||||||
if (!this.loaded) {
|
if (!this.loaded) {
|
||||||
throw new Error('You could not save the store before loaded from the storage.');
|
throw new Error(
|
||||||
|
'You could not save the store before loaded from the storage.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default class Contact extends TenantModel {
|
|||||||
* Defined virtual attributes.
|
* Defined virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['contactNormal', 'closingBalance'];
|
return ['contactNormal', 'closingBalance', 'formattedContactService'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,17 +35,36 @@ export default class Contact extends TenantModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the contact noraml;
|
* Retrieve the contact normal by the given contact service.
|
||||||
|
* @param {string} contactService
|
||||||
|
*/
|
||||||
|
static getFormattedContactService(contactService) {
|
||||||
|
const types = {
|
||||||
|
'customer': 'Customer',
|
||||||
|
'vendor': 'Vendor',
|
||||||
|
};
|
||||||
|
return types[contactService];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the contact normal.
|
||||||
*/
|
*/
|
||||||
get contactNormal() {
|
get contactNormal() {
|
||||||
return Contact.getContactNormalByType(this.contactService);
|
return Contact.getContactNormalByType(this.contactService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted contact service.
|
||||||
|
*/
|
||||||
|
get formattedContactService() {
|
||||||
|
return Contact.getFormattedContactService(this.contactService);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closing balance attribute.
|
* Closing balance attribute.
|
||||||
*/
|
*/
|
||||||
get closingBalance() {
|
get closingBalance() {
|
||||||
return this.openingBalance + this.balance;
|
return this.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default class Customer extends TenantModel {
|
|||||||
* Closing balance attribute.
|
* Closing balance attribute.
|
||||||
*/
|
*/
|
||||||
get closingBalance() {
|
get closingBalance() {
|
||||||
return this.openingBalance + this.balance;
|
return this.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default class Vendor extends TenantModel {
|
|||||||
* Closing balance attribute.
|
* Closing balance attribute.
|
||||||
*/
|
*/
|
||||||
get closingBalance() {
|
get closingBalance() {
|
||||||
return this.openingBalance + this.balance;
|
return this.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -491,59 +491,6 @@ export default class ItemsService implements IItemsService {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the given items IDs exists or throw not found service error.
|
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {number[]} itemsIDs -
|
|
||||||
* @return {Promise<void>}
|
|
||||||
*/
|
|
||||||
private async validateItemsIdsExists(
|
|
||||||
tenantId: number,
|
|
||||||
itemsIDs: number[]
|
|
||||||
): Promise<void> {
|
|
||||||
const { Item } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
const storedItems = await Item.query().whereIn('id', itemsIDs);
|
|
||||||
const storedItemsIds = storedItems.map((t: IItem) => t.id);
|
|
||||||
|
|
||||||
const notFoundItemsIds = difference(itemsIDs, storedItemsIds);
|
|
||||||
|
|
||||||
if (notFoundItemsIds.length > 0) {
|
|
||||||
throw new ServiceError(ERRORS.ITEMS_NOT_FOUND, null, {
|
|
||||||
notFoundItemsIds,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes items in bulk.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {number[]} itemsIds - Items ids.
|
|
||||||
*/
|
|
||||||
public async bulkDeleteItems(tenantId: number, itemsIds: number[]) {
|
|
||||||
const { Item } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
this.logger.info('[items] trying to delete items in bulk.', {
|
|
||||||
tenantId,
|
|
||||||
itemsIds,
|
|
||||||
});
|
|
||||||
// Validates the given items exist on the storage.
|
|
||||||
await this.validateItemsIdsExists(tenantId, itemsIds);
|
|
||||||
|
|
||||||
// Validate the item has no associated inventory transactions.
|
|
||||||
await this.validateHasNoInventoryAdjustments(tenantId, itemsIds);
|
|
||||||
|
|
||||||
// Validate the items have no associated invoices or bills.
|
|
||||||
await this.validateHasNoInvoicesOrBills(tenantId, itemsIds);
|
|
||||||
|
|
||||||
await Item.query().whereIn('id', itemsIds).delete();
|
|
||||||
|
|
||||||
this.logger.info('[items] deleted successfully in bulk.', {
|
|
||||||
tenantId,
|
|
||||||
itemsIds,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve items datatable list.
|
* Retrieve items datatable list.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
|
|||||||
@@ -30,4 +30,24 @@ export default class SettingsService {
|
|||||||
await settings.save();
|
await settings.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the given options is defined or either not.
|
||||||
|
* @param {Array} options
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
validateNotDefinedSettings(tenantId: number, options) {
|
||||||
|
const notDefined = [];
|
||||||
|
|
||||||
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
|
options.forEach((option) => {
|
||||||
|
const setting = settings.config.getMetaConfig(option.key, option.group);
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
notDefined.push(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return notDefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user