mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +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)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/',
|
||||
[...this.validateBulkSelectSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.bulkDeleteItems.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
[...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.
|
||||
* @param {Error} error
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import { Service } from 'typedi';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { body, query } from 'express-validator';
|
||||
import { pick } from 'lodash';
|
||||
import { IOptionDTO, IOptionsDTO } from 'interfaces';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import {
|
||||
getDefinedOptions,
|
||||
isDefinedOptionConfigurable,
|
||||
} from 'utils';
|
||||
import { getDefinedOptions, isDefinedOptionConfigurable } from 'utils';
|
||||
import SettingsService from 'services/Settings/SettingsService';
|
||||
|
||||
@Service()
|
||||
export default class SettingsController extends BaseController{
|
||||
export default class SettingsController extends BaseController {
|
||||
@Inject()
|
||||
settingsService: SettingsService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post('/',
|
||||
router.post(
|
||||
'/',
|
||||
this.saveSettingsValidationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.saveSettings.bind(this)),
|
||||
asyncMiddleware(this.saveSettings.bind(this))
|
||||
);
|
||||
router.get('/',
|
||||
router.get(
|
||||
'/',
|
||||
this.getSettingsSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSettings.bind(this)),
|
||||
asyncMiddleware(this.getSettings.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
@@ -46,7 +49,7 @@ export default class SettingsController extends BaseController{
|
||||
/**
|
||||
* Retrieve the application options from the storage.
|
||||
*/
|
||||
private get getSettingsSchema() {
|
||||
private get getSettingsSchema() {
|
||||
return [
|
||||
query('key').optional().trim().escape(),
|
||||
query('group').optional().trim().escape(),
|
||||
@@ -55,16 +58,19 @@ export default class SettingsController extends BaseController{
|
||||
|
||||
/**
|
||||
* Saves the given options to the storage.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
public async saveSettings(req: Request, res: Response, next) {
|
||||
const { Option } = req.models;
|
||||
const { tenantId } = req;
|
||||
const optionsDTO: IOptionsDTO = this.matchedBodyData(req);
|
||||
const { settings } = req;
|
||||
|
||||
const errorReasons: { type: string, code: number, keys: [] }[] = [];
|
||||
const notDefinedOptions = Option.validateDefined(optionsDTO.options);
|
||||
const errorReasons: { type: string; code: number; keys: [] }[] = [];
|
||||
const notDefinedOptions = this.settingsService.validateNotDefinedSettings(
|
||||
tenantId,
|
||||
optionsDTO.options
|
||||
);
|
||||
|
||||
if (notDefinedOptions.length) {
|
||||
errorReasons.push({
|
||||
@@ -82,7 +88,7 @@ export default class SettingsController extends BaseController{
|
||||
try {
|
||||
await settings.save();
|
||||
|
||||
return res.status(200).send({
|
||||
return res.status(200).send({
|
||||
type: 'success',
|
||||
code: 'OPTIONS.SAVED.SUCCESSFULLY',
|
||||
message: 'Options have been saved successfully.',
|
||||
@@ -94,8 +100,8 @@ export default class SettingsController extends BaseController{
|
||||
|
||||
/**
|
||||
* Retrieve settings.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
public getSettings(req: Request, res: Response) {
|
||||
const { settings } = req;
|
||||
@@ -103,4 +109,4 @@ export default class SettingsController extends BaseController{
|
||||
|
||||
return res.status(200).send({ settings: allSettings });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,166 +1,127 @@
|
||||
export default {
|
||||
organization: [
|
||||
{
|
||||
key: "name",
|
||||
type: "string",
|
||||
config: true,
|
||||
},
|
||||
{
|
||||
key: "base_currency",
|
||||
type: "string",
|
||||
config: true,
|
||||
},
|
||||
{
|
||||
key: "industry",
|
||||
organization: {
|
||||
name: {
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
key: "location",
|
||||
base_currency: {
|
||||
type: 'string',
|
||||
},
|
||||
industry: {
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
key: "fiscal_year",
|
||||
type: "string",
|
||||
// config: true,
|
||||
},
|
||||
{
|
||||
key: "financial_date_start",
|
||||
location: {
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
key: "language",
|
||||
type: "string",
|
||||
config: true,
|
||||
fiscal_year: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: "time_zone",
|
||||
type: "string",
|
||||
financial_date_start: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: "date_format",
|
||||
type: "string",
|
||||
language: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: 'accounting_basis',
|
||||
time_zone: {
|
||||
type: 'string',
|
||||
},
|
||||
date_format: {
|
||||
type: 'string',
|
||||
},
|
||||
accounting_basis: {
|
||||
type: 'string',
|
||||
}
|
||||
],
|
||||
manual_journals: [
|
||||
{
|
||||
key: "next_number",
|
||||
type: "string",
|
||||
},
|
||||
manual_journals: {
|
||||
next_number: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: "number_prefix",
|
||||
type: "string",
|
||||
number_prefix: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: "auto_increment",
|
||||
type: "boolean",
|
||||
auto_increment: {
|
||||
type: 'boolean',
|
||||
}
|
||||
],
|
||||
bill_payments: [
|
||||
{
|
||||
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',
|
||||
},
|
||||
bill_payments: {
|
||||
withdrawal_account: {
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
key: 'advance_deposit',
|
||||
key: 'string'
|
||||
},
|
||||
sales_estimates: {
|
||||
next_number: {
|
||||
type: 'string',
|
||||
},
|
||||
number_prefix: {
|
||||
type: 'string',
|
||||
},
|
||||
auto_increment: {
|
||||
type: "boolean",
|
||||
}
|
||||
],
|
||||
items: [
|
||||
{
|
||||
key: "sell_account",
|
||||
type: "number",
|
||||
},
|
||||
sales_receipts: {
|
||||
next_number: {
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
key: "cost_account",
|
||||
type: "number",
|
||||
number_prefix: {
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
key: "inventory_account",
|
||||
type: "number",
|
||||
auto_increment: {
|
||||
type: "boolean",
|
||||
},
|
||||
],
|
||||
expenses: [
|
||||
{
|
||||
key: "preferred_payment_account",
|
||||
preferred_deposit_account: {
|
||||
type: "number",
|
||||
}
|
||||
},
|
||||
sales_invoices: {
|
||||
next_number: {
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
accounts: [
|
||||
{
|
||||
key: 'account_code_required',
|
||||
number_prefix: {
|
||||
type: "string",
|
||||
},
|
||||
auto_increment: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
payment_receives: {
|
||||
next_number: {
|
||||
type: "string",
|
||||
},
|
||||
number_prefix: {
|
||||
type: "string",
|
||||
},
|
||||
auto_increment: {
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
key: 'account_code_unique',
|
||||
deposit_account: {
|
||||
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',
|
||||
},
|
||||
]
|
||||
account_code_unique: {
|
||||
type: 'boolean',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,14 +61,10 @@ export interface IItemDTO {
|
||||
}
|
||||
|
||||
export interface IItemsService {
|
||||
bulkDeleteItems(tenantId: number, itemsIds: number[]): Promise<void>;
|
||||
|
||||
getItem(tenantId: number, itemId: number): Promise<IItem>;
|
||||
deleteItem(tenantId: number, itemId: number): Promise<void>;
|
||||
editItem(tenantId: number, itemId: number, itemDTO: IItemDTO): Promise<IItem>;
|
||||
|
||||
newItem(tenantId: number, itemDTO: IItemDTO): Promise<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,19 +1,19 @@
|
||||
import { Model } from 'objection';
|
||||
import {
|
||||
IMetadata,
|
||||
IMetableStoreStorage,
|
||||
} from 'interfaces';
|
||||
import { IMetadata, IMetableStoreStorage } from 'interfaces';
|
||||
import MetableStore from './MetableStore';
|
||||
import { isBlank } from 'utils';
|
||||
|
||||
export default class MetableDBStore extends MetableStore implements IMetableStoreStorage{
|
||||
import { isBlank, parseBoolean } from 'utils';
|
||||
import MetableConfig from './MetableConfig';
|
||||
import config from 'data/options'
|
||||
export default class MetableDBStore
|
||||
extends MetableStore
|
||||
implements IMetableStoreStorage {
|
||||
repository: any;
|
||||
KEY_COLUMN: string;
|
||||
VALUE_COLUMN: string;
|
||||
TYPE_COLUMN: string;
|
||||
extraQuery: Function;
|
||||
loaded: Boolean;
|
||||
|
||||
config: MetableConfig;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
@@ -32,11 +32,12 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
...this.transfromMetaExtraColumns(meta),
|
||||
};
|
||||
};
|
||||
this.config = new MetableConfig(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes meta query.
|
||||
* @param {IMetadata} meta
|
||||
* @param {IMetadata} meta
|
||||
*/
|
||||
private transfromMetaExtraColumns(meta: IMetadata) {
|
||||
return this.extraColumns.reduce((obj, column) => {
|
||||
@@ -56,10 +57,10 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
setRepository(repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a extra query callback.
|
||||
* @param callback
|
||||
* @param callback
|
||||
*/
|
||||
setExtraQuery(callback) {
|
||||
this.extraQuery = callback;
|
||||
@@ -84,17 +85,18 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
* @returns {Promise}
|
||||
*/
|
||||
saveUpdated(metadata: IMetadata[]) {
|
||||
const updated = metadata.filter((m) => (m._markAsUpdated === true));
|
||||
const updated = metadata.filter((m) => m._markAsUpdated === true);
|
||||
const opers = [];
|
||||
|
||||
updated.forEach((meta) => {
|
||||
const updateOper = this.repository.update({
|
||||
[this.VALUE_COLUMN]: meta.value,
|
||||
}, {
|
||||
...this.extraQuery(meta),
|
||||
}).then(() => {
|
||||
meta._markAsUpdated = false;
|
||||
});
|
||||
const updateOper = this.repository
|
||||
.update(
|
||||
{ [this.VALUE_COLUMN]: meta.value },
|
||||
{ ...this.extraQuery(meta) }
|
||||
)
|
||||
.then(() => {
|
||||
meta._markAsUpdated = false;
|
||||
});
|
||||
opers.push(updateOper);
|
||||
});
|
||||
return Promise.all(opers);
|
||||
@@ -106,16 +108,20 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
* @returns {Promise}
|
||||
*/
|
||||
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> = [];
|
||||
|
||||
if (deleted.length > 0) {
|
||||
deleted.forEach((meta) => {
|
||||
const deleteOper = this.repository.deleteBy({
|
||||
...this.extraQuery(meta),
|
||||
}).then(() => {
|
||||
meta._markAsDeleted = false;
|
||||
});
|
||||
const deleteOper = this.repository
|
||||
.deleteBy({
|
||||
...this.extraQuery(meta),
|
||||
})
|
||||
.then(() => {
|
||||
meta._markAsDeleted = false;
|
||||
});
|
||||
opers.push(deleteOper);
|
||||
});
|
||||
}
|
||||
@@ -128,7 +134,9 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
* @returns {Promise}
|
||||
*/
|
||||
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> = [];
|
||||
|
||||
inserted.forEach((meta) => {
|
||||
@@ -137,10 +145,9 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
[this.VALUE_COLUMN]: meta.value,
|
||||
...this.transfromMetaExtraColumns(meta),
|
||||
};
|
||||
const insertOper = this.repository.create(insertData)
|
||||
.then(() => {
|
||||
meta._markAsInserted = false;
|
||||
});
|
||||
const insertOper = this.repository.create(insertData).then(() => {
|
||||
meta._markAsInserted = false;
|
||||
});
|
||||
opers.push(insertOper);
|
||||
});
|
||||
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} valueType -
|
||||
* @return {String|Number|Boolean} -
|
||||
*/
|
||||
static formatMetaValue(value: string|number|boolean, valueType: string|false) {
|
||||
let parsedValue: string|number|boolean;
|
||||
static parseMetaValue(
|
||||
value: string,
|
||||
valueType: string | false
|
||||
): string | boolean | number {
|
||||
let parsedValue: string | number | boolean;
|
||||
|
||||
switch (valueType) {
|
||||
case 'number':
|
||||
parsedValue = `${value}`;
|
||||
parsedValue = parseFloat(value);
|
||||
break;
|
||||
case 'boolean':
|
||||
parsedValue = value ? '1' : '0';
|
||||
parsedValue = parseBoolean(value, false);
|
||||
break;
|
||||
case 'json':
|
||||
parsedValue = JSON.stringify(parsedValue);
|
||||
@@ -195,11 +205,15 @@ export default class MetableDBStore extends MetableStore implements IMetableStor
|
||||
* @param {String} parseType -
|
||||
*/
|
||||
mapMetadata(metadata: IMetadata) {
|
||||
const metaType = this.config.getMetaType(
|
||||
metadata[this.KEY_COLUMN],
|
||||
metadata['group'],
|
||||
);
|
||||
return {
|
||||
key: metadata[this.KEY_COLUMN],
|
||||
value: MetableDBStore.formatMetaValue(
|
||||
value: MetableDBStore.parseMetaValue(
|
||||
metadata[this.VALUE_COLUMN],
|
||||
this.TYPE_COLUMN ? metadata[this.TYPE_COLUMN] : false,
|
||||
metaType
|
||||
),
|
||||
...this.extraColumns.reduce((obj, extraCol: string) => {
|
||||
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.
|
||||
*/
|
||||
private validateStoreIsLoaded() {
|
||||
if (!this.loaded) {
|
||||
throw new Error('You could not save the store before loaded from the storage.');
|
||||
if (!this.loaded) {
|
||||
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.
|
||||
*/
|
||||
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() {
|
||||
return Contact.getContactNormalByType(this.contactService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve formatted contact service.
|
||||
*/
|
||||
get formattedContactService() {
|
||||
return Contact.getFormattedContactService(this.contactService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closing balance attribute.
|
||||
*/
|
||||
get closingBalance() {
|
||||
return this.openingBalance + this.balance;
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,7 @@ export default class Customer extends TenantModel {
|
||||
* Closing balance attribute.
|
||||
*/
|
||||
get closingBalance() {
|
||||
return this.openingBalance + this.balance;
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class Vendor extends TenantModel {
|
||||
* Closing balance attribute.
|
||||
*/
|
||||
get closingBalance() {
|
||||
return this.openingBalance + this.balance;
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -491,59 +491,6 @@ export default class ItemsService implements IItemsService {
|
||||
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.
|
||||
* @param {number} tenantId
|
||||
|
||||
@@ -30,4 +30,24 @@ export default class SettingsService {
|
||||
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