Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2488-jasmine_true_random

This commit is contained in:
Carlos Jimenez Ruiz 2020-10-05 16:09:30 +02:00
commit 27d2c59358
24 changed files with 248 additions and 132 deletions

View File

@ -4,3 +4,4 @@ ADD COLUMN `oldInstance` text CHARACTER SET utf8 COLLATE utf8_unicode_ci,
ADD COLUMN `newInstance` text CHARACTER SET utf8 COLLATE utf8_unicode_ci,
ADD COLUMN `changedModelId` int(11) DEFAULT NULL,
ADD COLUMN `changedModelValue` varchar(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL;

View File

@ -0,0 +1,9 @@
ALTER TABLE `vn`.`ticketService`
DROP FOREIGN KEY `ticketServiceIvaGroup`;
ALTER TABLE `vn`.`ticketService`
CHANGE COLUMN `taxClassFk` `taxClassFk` TINYINT(3) UNSIGNED NOT NULL DEFAULT 2 ;
ALTER TABLE `vn`.`ticketService`
ADD CONSTRAINT `ticketServiceIvaGroup`
FOREIGN KEY (`taxClassFk`)
REFERENCES `vn`.`taxClass` (`id`)
ON UPDATE CASCADE;

View File

@ -0,0 +1 @@
UPDATE salix.ACL t SET t.principalId = 'salesAssistant' WHERE t.id = 234

View File

@ -0,0 +1,4 @@
ALTER TABLE `vn`.department
ADD notificationEmail VARCHAR(150) null;
UPDATE vn.department t SET t.notificationEmail = 'direccioncomercial@verdnatura.es' WHERE t.id = 43

View File

@ -42,8 +42,8 @@ INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossF
FROM `vn`.`user`;
UPDATE `vn`.`worker` SET bossFk = NULL WHERE id = 20;
UPDATE `vn`.`worker` SET bossFk = 20
WHERE id = 1 OR id = 9;
UPDATE `vn`.`worker` SET bossFk = 20 WHERE id = 1 OR id = 9;
UPDATE `vn`.`worker` SET bossFk = 19 WHERE id = 18;
DELETE FROM `vn`.`worker` WHERE firstName ='customer';
@ -479,7 +479,9 @@ INSERT INTO `vn`.`expence`(`id`, `taxTypeFk`, `name`, `isWithheld`)
(4751000000, 1, 'Retenciones', 1),
(4751000000, 6, 'Retencion', 0),
(6210000567, 0, 'Alquiler VNH', 0),
(7001000000, 1, 'Mercaderia', 0);
(7001000000, 1, 'Mercaderia', 0),
(7050000000, 1, 'Prestacion de servicios', 1);
INSERT INTO `vn`.`invoiceOutExpence`(`id`, `invoiceOutFk`, `amount`, `expenceFk`, `created`)
VALUES
@ -1694,6 +1696,10 @@ UPDATE `postgresql`.`business_labour` bl
SET bl.`professional_category_id` = 31
WHERE p.`Id_trabajador` = 110;
UPDATE `postgresql`.`business_labour` bl
SET bl.`department_id` = 43
WHERE business_id IN(18, 19);
INSERT INTO `postgresql`.`media`(`media_id`, `media_type_id`, `value`, `sort`)
VALUES
(1, 10, 600123321, 0),

View File

@ -510,10 +510,8 @@ export default {
firstServiceType: 'vn-ticket-service vn-autocomplete[ng-model="service.ticketServiceTypeFk"]',
firstQuantity: 'vn-ticket-service vn-input-number[ng-model="service.quantity"]',
firstPrice: 'vn-ticket-service vn-horizontal:nth-child(1) vn-input-number[ng-model="service.price"]',
firstVatType: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
newServiceTypeName: '.vn-dialog.shown vn-textfield[ng-model="newServiceType.name"]',
newServiceTypeExpense: '.vn-dialog.shown vn-autocomplete[ng-model="newServiceType.expenseFk"]',
serviceLine: 'vn-ticket-service > form > vn-card > vn-one:nth-child(2) > vn-horizontal',
saveServiceButton: 'button[type=submit]',
saveServiceTypeButton: '.vn-dialog.shown tpl-buttons > button'

View File

@ -78,7 +78,6 @@ describe('Ticket services path', () => {
it('should create a new service type then add price then create the service', async() => {
await page.write(selectors.ticketService.newServiceTypeName, 'Documentos');
await page.autocompleteSearch(selectors.ticketService.newServiceTypeExpense, 'Retencion');
await page.waitToClick(selectors.ticketService.saveServiceTypeButton);
await page.write(selectors.ticketService.firstPrice, '999');
await page.waitToClick(selectors.ticketService.saveServiceButton);
@ -109,13 +108,6 @@ describe('Ticket services path', () => {
expect(result).toEqual('999');
});
it('should confirm the service VAT was created correctly', async() => {
const result = await page
.waitToGetProperty(selectors.ticketService.firstVatType, 'value');
expect(result).toEqual('General VAT');
});
it('should delete the service', async() => {
await page.waitToClick(selectors.ticketService.fistDeleteServiceButton);
await page.waitForNumberOfElements(selectors.ticketService.serviceLine, 0);

View File

@ -42,6 +42,7 @@ describe('Ticket create path', () => {
it('should again open the new ticket form', async() => {
await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
await page.waitFor(500);
await page.waitToClick(selectors.ticketsIndex.newTicketButton);
await page.waitForState('ticket.create');
});

View File

@ -20,14 +20,13 @@ describe('Entry create path', () => {
await page.waitForState('entry.create');
});
it('should fill the form to create a valid entry', async() => {
it('should fill the form to create a valid entry then redirect to basic Data', async() => {
await page.autocompleteSearch(selectors.entryIndex.newEntrySupplier, '2');
await page.autocompleteSearch(selectors.entryIndex.newEntryTravel, 'Warehouse Three');
await page.autocompleteSearch(selectors.entryIndex.newEntryCompany, 'ORN');
await page.waitToClick(selectors.entryIndex.saveNewEntry);
});
it('should be redirected to entry basic data', async() => {
await page.waitToClick(selectors.entryIndex.saveNewEntry);
await page.waitFor(500);
await page.waitForState('entry.card.basicData');
});
});

View File

@ -68,5 +68,7 @@
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",
"Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment",
"NOT_ZONE_WITH_THIS_PARAMETERS": "NOT_ZONE_WITH_THIS_PARAMETERS"
"NOT_ZONE_WITH_THIS_PARAMETERS": "NOT_ZONE_WITH_THIS_PARAMETERS",
"Created absence": "The worker <strong>{{author}}</strong> has added an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.",
"Deleted absence": "The worker <strong>{{author}}</strong> has deleted an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}."
}

View File

@ -143,5 +143,8 @@
"Role name must be written in camelCase": "Role name must be written in camelCase",
"can't be set": "can't be set",
"Email already exists": "Email already exists",
"User already exists": "User already exists"
"User already exists": "User already exists",
"Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}."
}

View File

@ -186,10 +186,16 @@ module.exports = Self => {
let payMethodWithIban = 4;
// Validate socialName format
const socialNameChanged = orgData && changes
&& orgData.socialName != changes.socialName;
const hasChanges = orgData && changes;
const socialName = changes.socialName || orgData.socialName;
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
if (socialNameChanged && !isAlpha(changes.socialName))
const socialNameChanged = hasChanges
&& orgData.socialName != socialName;
const dataCheckedChanged = hasChanges
&& orgData.isTaxDataChecked != isTaxDataChecked;
if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName))
throw new UserError('The socialName has an invalid format');
if (changes.salesPerson === null) {

View File

@ -9,30 +9,6 @@
</vn-watcher>
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
label="Reference"
ng-model="$ctrl.entry.ref"
rule
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Notes"
ng-model="$ctrl.entry.notes"
rule
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Observation"
ng-model="$ctrl.entry.observation"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
@ -63,6 +39,30 @@
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Reference"
ng-model="$ctrl.entry.ref"
rule
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Notes"
ng-model="$ctrl.entry.notes"
rule
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Observation"
ng-model="$ctrl.entry.observation"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one

View File

@ -20,34 +20,33 @@ module.exports = Self => {
});
Self.getComponentsSum = async id => {
const models = Self.app.models;
let salesComponents = [];
let componentsSum = [];
let sales = await models.Sale.find({where: {ticketFk: id}});
let components = await models.Component.find();
if (sales.length > 0) {
for (let sale of sales) {
let myComponents = await models.SaleComponent.find({where: {saleFk: sale.id}});
salesComponents = salesComponents.concat(myComponents);
}
salesComponents.reduce((acumulator, currentValue) => {
if (!acumulator[currentValue.componentFk]) {
let defaultValue = 0;
let tarjetComponent = components.find(component => component.id === currentValue.componentFk);
acumulator[currentValue.componentFk] = {
componentFk: currentValue.componentFk,
value: defaultValue,
name: tarjetComponent.name
};
componentsSum.push(acumulator[currentValue.componentFk]);
let sales = await models.Sale.find({
include: {
relation: 'components',
scope: {fields: ['value', 'componentFk'],
include: {
relation: 'component',
}
}
acumulator[currentValue.componentFk].value += currentValue.value;
return acumulator;
});
},
where: {ticketFk: id}
});
for (let sale of sales) {
for (let component of sale.components()) {
let componentId = componentsSum[component.componentFk];
if (!componentId) {
componentsSum[component.componentFk] = {
componentFk: component.componentFk,
value: 0,
name: component.component().name
};
}
componentsSum[component.componentFk].value += component.value * sale.quantity;
}
}
return componentsSum;
return componentsSum.filter(component => {
return component != null;
});
};
};

View File

@ -17,7 +17,6 @@
},
"expenseFk": {
"type": "Number",
"required": true,
"mysql": {
"columnName": "expenceFk"
}

View File

@ -53,13 +53,6 @@
ng-model="service.price"
step="0.01">
</vn-input-number>
<vn-autocomplete vn-one
data="taxClasses"
label="Tax class"
show-field="description"
value-field="id"
ng-model="service.taxClassFk">
</vn-autocomplete>
<vn-auto>
<vn-icon-button
pointer
@ -97,17 +90,6 @@
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
vn-focus
url="Expenses"
label="Expense"
show-field="name"
value-field="id"
ng-model="newServiceType.expenseFk">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>

View File

@ -31,6 +31,7 @@ module.exports = Self => {
Self.createAbsence = async(ctx, id, absenceTypeId, dated) => {
const models = Self.app.models;
const $t = ctx.req.__; // $translate
const userId = ctx.req.accessToken.userId;
const isSubordinate = await models.Worker.isSubordinate(ctx, id);
const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss');
@ -39,6 +40,7 @@ module.exports = Self => {
throw new UserError(`You don't have enough privileges`);
const labour = await models.WorkerLabour.findOne({
include: {relation: 'department'},
where: {
and: [
{workerFk: id},
@ -49,10 +51,42 @@ module.exports = Self => {
}
});
return models.Calendar.create({
const absence = await models.Calendar.create({
businessFk: labour.businessFk,
dayOffTypeFk: absenceTypeId,
dated: dated
});
const department = labour.department();
if (department && department.notificationEmail) {
const absenceType = await models.AbsenceType.findById(absenceTypeId);
const account = await models.Account.findById(userId);
const subordinated = await models.Account.findById(id);
const origin = ctx.req.headers.origin;
const body = $t('Created absence', {
author: account.nickname,
employee: subordinated.nickname,
absenceType: absenceType.name,
dated: formatDate(dated),
workerUrl: `${origin}/#!/worker/${id}/calendar`
});
await models.Mail.create({
subject: $t('Absence change notification on the labour calendar'),
body: body,
sender: department.notificationEmail
});
}
return absence;
};
function formatDate(date) {
let day = date.getDate();
if (day < 10) day = `0${day}`;
let month = date.getMonth();
if (month < 10) month = `0${month}`;
let year = date.getFullYear();
return `${day}-${month}-${year}`;
}
};

View File

@ -23,6 +23,7 @@ module.exports = Self => {
Self.deleteAbsence = async(ctx, id, absenceId) => {
const models = Self.app.models;
const $t = ctx.req.__; // $translate
const userId = ctx.req.accessToken.userId;
const isSubordinate = await models.Worker.isSubordinate(ctx, id);
const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss');
@ -30,8 +31,46 @@ module.exports = Self => {
if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss))
throw new UserError(`You don't have enough privileges`);
const absence = await models.Calendar.findById(absenceId);
const absence = await models.Calendar.findById(absenceId, {
include: {
relation: 'labour',
scope: {
include: {relation: 'department'}
}
}
});
const result = await absence.destroy();
const labour = absence.labour();
const department = labour && labour.department();
if (department && department.notificationEmail) {
const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk);
const account = await models.Account.findById(userId);
const subordinated = await models.Account.findById(labour.workerFk);
const origin = ctx.req.headers.origin;
const body = $t('Deleted absence', {
author: account.nickname,
employee: subordinated.nickname,
absenceType: absenceType.name,
dated: formatDate(absence.dated),
workerUrl: `${origin}/#!/worker/${id}/calendar`
});
await models.Mail.create({
subject: $t('Absence change notification on the labour calendar'),
body: body,
sender: department.notificationEmail
});
}
return absence.destroy();
return result;
};
function formatDate(date) {
let day = date.getDate();
if (day < 10) day = `0${day}`;
let month = date.getMonth();
if (month < 10) month = `0${month}`;
let year = date.getFullYear();
return `${day}-${month}-${year}`;
}
};

View File

@ -1,7 +1,8 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('Worker createAbsence()', () => {
const workerId = 106;
const workerId = 18;
let createdAbsence;
afterAll(async() => {
@ -10,7 +11,7 @@ describe('Worker createAbsence()', () => {
});
it('should return an error for a user without enough privileges', async() => {
const ctx = {req: {accessToken: {userId: 106}}};
const ctx = {req: {accessToken: {userId: 18}}};
const absenceTypeId = 1;
const dated = new Date();
@ -25,12 +26,23 @@ describe('Worker createAbsence()', () => {
});
it('should create a new absence', async() => {
const ctx = {req: {accessToken: {userId: 37}}};
const activeCtx = {
accessToken: {userId: 19},
headers: {origin: 'http://localhost'}
};
const ctx = {req: activeCtx};
ctx.req.__ = value => {
return value;
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
const absenceTypeId = 1;
const dated = new Date();
createdAbsence = await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated);
const expectedBusinessId = 106;
const expectedBusinessId = 18;
const expectedAbsenceTypeId = 1;
expect(createdAbsence.businessFk).toEqual(expectedBusinessId);

View File

@ -1,12 +1,25 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('Worker deleteAbsence()', () => {
const workerId = 106;
const workerId = 18;
let createdAbsence;
const activeCtx = {
accessToken: {userId: 19},
headers: {origin: 'http://localhost'}
};
const ctx = {req: activeCtx};
ctx.req.__ = value => {
return value;
};
it('should return an error for a user without enough privileges', async() => {
const ctx = {req: {accessToken: {userId: 106}}};
const businessId = 106;
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
activeCtx.accessToken.userId = 106;
const businessId = 18;
createdAbsence = await app.models.Calendar.create({
businessFk: businessId,
dayOffTypeFk: 1,
@ -14,7 +27,7 @@ describe('Worker deleteAbsence()', () => {
});
let error;
await app.models.Worker.deleteAbsence(ctx, workerId, createdAbsence.id).catch(e => {
await app.models.Worker.deleteAbsence(ctx, 18, createdAbsence.id).catch(e => {
error = e;
}).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`);
@ -24,8 +37,12 @@ describe('Worker deleteAbsence()', () => {
});
it('should create a new absence', async() => {
const ctx = {req: {accessToken: {userId: 37}}};
const businessId = 106;
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
activeCtx.accessToken.userId = 19;
const businessId = 18;
expect(createdAbsence.businessFk).toEqual(businessId);

View File

@ -1,6 +1,10 @@
{
"name": "Calendar",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "WorkerLog",
"relation": "labour"
},
"options": {
"mysql": {
"table": "calendar"
@ -23,6 +27,11 @@
"type": "belongsTo",
"model": "AbsenceType",
"foreignKey": "dayOffTypeFk"
},
"labour": {
"type": "belongsTo",
"model": "WorkerLabour",
"foreignKey": "businessFk"
}
}
}

View File

@ -28,6 +28,9 @@
},
"chatName": {
"type": "String"
},
"notificationEmail": {
"type": "String"
}
}
}

View File

@ -63,7 +63,7 @@
"state": "worker.card.workerLog",
"component": "vn-worker-log",
"description": "Log",
"acl": ["hr"]
"acl": ["salesAssistant"]
}, {
"url": "/pbx",
"state": "worker.card.pbx",

52
package-lock.json generated
View File

@ -5835,7 +5835,7 @@
},
"util": {
"version": "0.10.3",
"resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
"dev": true,
"requires": {
@ -6806,7 +6806,7 @@
"base": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
"integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
"integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=",
"dev": true,
"requires": {
"cache-base": "^1.0.1",
@ -6916,9 +6916,9 @@
"dev": true
},
"bl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
@ -7329,7 +7329,7 @@
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
"integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
"integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=",
"dev": true,
"requires": {
"collection-visit": "^1.0.0",
@ -7543,7 +7543,7 @@
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
"integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
"integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=",
"dev": true,
"requires": {
"arr-union": "^3.1.0",
@ -9796,7 +9796,7 @@
},
"file-loader": {
"version": "1.1.11",
"resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
"dev": true,
"requires": {
@ -10982,7 +10982,7 @@
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=",
"dev": true,
"requires": {
"global-prefix": "^1.0.1",
@ -13207,7 +13207,7 @@
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=",
"dev": true,
"requires": {
"isobject": "^3.0.1"
@ -13541,7 +13541,7 @@
"jasmine-spec-reporter": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
"integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
"integrity": "sha1-HWMq7ANBZwrTJPkrqEtLMrNeniI=",
"dev": true,
"requires": {
"colors": "1.1.2"
@ -22227,7 +22227,7 @@
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
}
@ -22620,7 +22620,7 @@
},
"safe-regex": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
"dev": true,
"requires": {
@ -22834,7 +22834,7 @@
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"dev": true,
"requires": {
@ -22850,18 +22850,18 @@
"dev": true
},
"selfsigned": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
"integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
"version": "1.10.8",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
"integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
"dev": true,
"requires": {
"node-forge": "0.9.0"
"node-forge": "^0.10.0"
},
"dependencies": {
"node-forge": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
"integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
"dev": true
}
}
@ -23295,7 +23295,7 @@
"snapdragon-node": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
"integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
"integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=",
"dev": true,
"requires": {
"define-property": "^1.0.0",
@ -23346,7 +23346,7 @@
"snapdragon-util": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
"integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
"integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=",
"dev": true,
"requires": {
"kind-of": "^3.2.0"
@ -23630,7 +23630,7 @@
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
"integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
"integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=",
"dev": true,
"requires": {
"extend-shallow": "^3.0.0"
@ -24941,7 +24941,7 @@
"touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"integrity": "sha1-/jZfX3XsntTlaCXgu3bSSrdK+Ds=",
"dev": true,
"requires": {
"nopt": "~1.0.10"
@ -26733,7 +26733,7 @@
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
},
"xmlchars": {