fix backend unit test for ticket.weekly #393

Merged
joan merged 8 commits from 2457-fix_regularizeClaim_unit_test into dev 2020-09-29 12:33:00 +00:00
48 changed files with 3363 additions and 2224 deletions
Showing only changes of commit 6afee4924c - Show all commits

2
Jenkinsfile vendored
View File

@ -134,7 +134,7 @@ pipeline {
}
}
if (!env.COMMITTER_EMAIL) return
if (!env.COMMITTER_EMAIL || currentBuild.currentResult == 'SUCCESS') return;
try {
mail(
to: env.COMMITTER_EMAIL,

View File

@ -0,0 +1,178 @@
const Imap = require('imap');
module.exports = Self => {
Self.remoteMethod('checkInbox', {
description: 'Check an email inbox and process it',
accessType: 'READ',
returns:
{
arg: 'body',
type: 'file',
root: true
},
http: {
path: `/checkInbox`,
verb: 'GET'
}
});
Self.checkInbox = async() => {
let imapConfig = await Self.app.models.WorkerTimeControlParams.findOne();
let imap = new Imap({
user: imapConfig.mailUser,
password: imapConfig.mailPass,
host: imapConfig.mailHost,
port: 993,
tls: true
});
let isEmailOk;
let uid;
let emailBody;
function openInbox(cb) {
imap.openBox('INBOX', true, cb);
}
imap.once('ready', function() {
openInbox(function(err, box) {
if (err) throw err;
let f = imap.seq.fetch('1:*', {
bodies: ['HEADER.FIELDS (FROM SUBJECT)', '1'],
struct: true
});
f.on('message', function(msg, seqno) {
isEmailOk = false;
msg.on('body', function(stream, info) {
let buffer = '';
let bufferCopy = '';
stream.on('data', function(chunk) {
buffer = chunk.toString('utf8');
if (info.which === '1' && bufferCopy.length == 0)
bufferCopy = buffer.replace(/\s/g, ' ');
});
stream.on('end', function() {
if (bufferCopy.length > 0) {
emailBody = bufferCopy.toUpperCase().trim();
const bodyPositionOK = emailBody.match(/\bOK\b/i);
if (bodyPositionOK != null && (bodyPositionOK.index == 0 || bodyPositionOK.index == 122))
isEmailOk = true;
else
isEmailOk = false;
}
});
msg.once('attributes', function(attrs) {
uid = attrs.uid;
});
msg.once('end', function() {
if (info.which === 'HEADER.FIELDS (FROM SUBJECT)') {
if (isEmailOk) {
imap.move(uid, 'exito', function(err) {
});
emailConfirm(buffer);
} else {
imap.move(uid, 'error', function(err) {
});
emailReply(buffer, emailBody);
}
}
});
});
});
f.once('end', function() {
imap.end();
});
});
});
imap.connect();
return 'Leer emails de gestion horaria';
};
async function emailConfirm(buffer) {
const now = new Date();
const from = JSON.stringify(Imap.parseHeader(buffer).from);
const subject = JSON.stringify(Imap.parseHeader(buffer).subject);
const timeControlDate = await getEmailDate(subject);
const week = timeControlDate[0];
const year = timeControlDate[1];
const user = await getUser(from);
let workerMail;
if (user.id != null) {
workerMail = await Self.app.models.WorkerTimeControlMail.findOne({
where: {
week: week,
year: year,
workerFk: user.id
}
});
}
if (workerMail != null) {
await workerMail.updateAttributes({
updated: now,
state: 'CONFIRMED'
});
}
}
async function emailReply(buffer, emailBody) {
const now = new Date();
const from = JSON.stringify(Imap.parseHeader(buffer).from);
const subject = JSON.stringify(Imap.parseHeader(buffer).subject);
const timeControlDate = await getEmailDate(subject);
const week = timeControlDate[0];
const year = timeControlDate[1];
const user = await getUser(from);
let workerMail;
if (user.id != null) {
workerMail = await Self.app.models.WorkerTimeControlMail.findOne({
where: {
week: week,
year: year,
workerFk: user.id
}
});
if (workerMail != null) {
await workerMail.updateAttributes({
updated: now,
state: 'REVISE',
emailResponse: emailBody
});
} else
await sendMail(user, subject, emailBody);
}
}
async function getUser(workerEmail) {
const userEmail = workerEmail.match(/(?<=<)(.*?)(?=>)/);
let [user] = await Self.rawSql(`SELECT u.id,u.name FROM account.user u
LEFT JOIN account.mailForward m on m.account = u.id
WHERE forwardTo =? OR
CONCAT(u.name,'@verdnatura.es') = ?`,
[userEmail[0], userEmail[0]]);
return user;
}
async function getEmailDate(subject) {
const date = subject.match(/\d+/g);
return date;
}
async function sendMail(user, subject, emailBody) {
const sendTo = 'rrhh@verdnatura.es';
const emailSubject = subject + ' ' + user.name;
await Self.app.models.Mail.create({
sender: sendTo,
subject: emailSubject,
body: emailBody
});
}
};

View File

@ -73,6 +73,12 @@
},
"UserLog": {
"dataSource": "vn"
},
"WorkerTimeControlParams": {
"dataSource": "vn"
},
"WorkerTimeControlMail": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,42 @@
{
"name": "WorkerTimeControlMail",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerTimeControlMail"
}
},
"properties": {
"id": {
"id": true,
"type": "Number",
"required": true
},
"workerFk": {
"type": "Number"
},
"year": {
"type": "Number"
},
"week": {
"type": "Number"
},
"state": {
"type": "String"
},
"updated": {
"type": "Date"
},
"emailResponse": {
"type": "String"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/imap-time-control/checkInbox')(Self);
};

View File

@ -0,0 +1,35 @@
{
"name": "WorkerTimeControlParams",
"description": "imap config",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerTimeControlParams"
}
},
"properties": {
"mailHost": {
"type": "String"
},
"mailUser": {
"type": "String"
},
"mailPass": {
"type": "String"
},
"mailSuccessFolder": {
"type": "String"
},
"mailErrorFolder": {
"type": "String"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -1,4 +1,4 @@
FROM mysql:8.0.18
FROM mariadb:10.4.13
ENV MYSQL_ROOT_PASSWORD root
ENV TZ Europe/Madrid
@ -7,9 +7,9 @@ ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \
&& curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \
&& echo "deb http://apt.verdnatura.es/ stretch main" > /etc/apt/sources.list.d/vn.list \
&& echo "deb http://apt.verdnatura.es/ jessie main" > /etc/apt/sources.list.d/vn.list \
&& apt-get update \
&& apt-get install -y vn-mysql libmysqlclient21 \
&& apt-get install -y vn-mariadb \
&& apt-get purge -y --auto-remove curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
@ -19,16 +19,16 @@ COPY docker/docker-init.sh docker/docker-start.sh /usr/local/bin/
RUN mkdir /mysql-data \
&& chown -R mysql:mysql /mysql-data
WORKDIR /docker-entrypoint-initdb.d
COPY dump /docker-boot/dump
COPY changes /docker-boot/changes
COPY import-changes.sh config.ini /docker-boot/
COPY dump dump
COPY docker/docker-boot.sh /docker-entrypoint-initdb.d/
COPY changes import/changes
COPY import-changes.sh config.ini import/
ARG STAMP=unknown
RUN gosu mysql docker-init.sh mysqld \
&& rm -rf /docker-entrypoint-initdb.d/*
&& rm -rf /docker-entrypoint-initdb.d/* \
&& rm -rf /docker-boot
USER mysql
ENTRYPOINT ["docker-start.sh"]

View File

@ -1,5 +1,6 @@
#!/bin/bash
cd /docker-boot
export MYSQL_PWD=root
mysql_import() {
@ -11,7 +12,7 @@ mysql_import() {
mysql_import dump/structure.sql
mysql_import dump/mysqlPlugins.sql
mysql_import dump/dumpedFixtures.sql
import/import-changes.sh
./import-changes.sh
mysql_import dump/fixtures.sql
echo "[INFO] -> Import finished"

View File

@ -13,5 +13,4 @@ docker_setup_db
docker_process_init_files /docker-entrypoint-initdb.d/*
mysql_expire_root_user
docker_temp_server_stop

View File

@ -2,9 +2,7 @@
innodb_log_file_size = 4M
innodb_autoextend_increment = 4
innodb_page_size = 8K
innodb_default_row_format = COMPACT
log_bin_trust_function_creators = ON
datadir = /mysql-data
sql_mode = NO_ENGINE_SUBSTITUTION
innodb_temp_data_file_path = /tmp/ibtmp1:12M:autoextend
skip-log-bin

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,6 @@
CREATE SCHEMA IF NOT EXISTS `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`;
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`zoneGeo` AUTO_INCREMENT = 1;
@ -776,10 +774,16 @@ INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `d
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 1, 4751000000, 0, NULL, 0, 82, 2, NULL, 0),
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 83, 2, NULL, 0),
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 84, 2, NULL, 0),
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 67350, 2, NULL, 0),
(16, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 67350, 2, NULL, 0),
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 88, 2, NULL, 0),
(16, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 0, 4751000000, 0, NULL, 0, 88, 2, NULL, 0),
(71, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 1, 4751000000, 0, NULL, 0, 88, 2, NULL, 0);
INSERT INTO `vn`.`priceFixed`(`id`, `itemFk`, `rate0`, `rate1`, `rate2`, `rate3`, `started`, `ended`, `bonus`, `warehouseFk`, `created`)
VALUES
(1, 1, 0, 0, 2.5, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()),
(2, 3, 10, 10, 10, 10, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()),
(3, 5, 8.5, 10, 7.5, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 1, 2, CURDATE());
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18),
@ -1169,7 +1173,9 @@ INSERT INTO `vn`.`itemTag`(`id`,`itemFk`,`tagFk`,`value`,`priority`)
(96, 14, 1, 'White', 5),
(97, 14, 67, 'supply', 6),
(98, 14, 23, '1', 7),
(99, 71, 92, 'Shipping cost', 2);
(99, 15, 92, 'Shipping cost', 2),
(100, 16, 92, 'Shipping cost', 2),
(101, 71, 92, 'Shipping cost', 2);
INSERT INTO `vn`.`itemTypeTag`(`id`, `itemTypeFk`, `tagFk`, `priority`)
VALUES
@ -1637,13 +1643,13 @@ INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `attenderFk
(4, 'Melee weapon combat first 15cm', 18, 35, 15, NULL, 1.30, NULL, NULL, 11, CURDATE()),
(5, 'Melee weapon combat first 15cm', 18, 35, 15, 4, 1.30, 0, NULL, 18, CURDATE());
INSERT INTO `vn`.`ticketServiceType`(`id`, `name`)
INSERT INTO `vn`.`ticketServiceType`(`id`, `name`, `expenceFk`)
VALUES
(1, 'Porte Agencia'),
(2, 'Portes Retorno'),
(3, 'Porte Carry'),
(4, 'Cargo FITOSANITARIO'),
(5, 'Documentos');
(1, 'Porte Agencia', 7001000000),
(2, 'Portes Retorno', 7001000000),
(3, 'Porte Carry', 7001000000),
(4, 'Cargo FITOSANITARIO', 4751000000),
(5, 'Documentos', 2000000000);
INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`, `ticketServiceTypeFk`)
VALUES

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,7 @@ TABLES=(
cplusTaxBreak
pgc
tag
time
claimResponsible
claimReason
claimRedelivery

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
// #2257 xdescribe dbtest workerTimeControl_check()
// #2261 xdescribe dbtest workerTimeControl_check()
xdescribe('worker workerTimeControl_check()', () => {
it(`should throw an error if the worker can't sign on that tablet`, async() => {
let stmts = [];

View File

@ -50,7 +50,7 @@ describe('Ticket Edit basic data path', () => {
});
it(`should edit the ticket agency then check there are no zones for it`, async() => {
await page.autocompleteSearch(selectors.ticketBasicData.agency, 'Entanglement');
await page.autocompleteSearch(selectors.ticketBasicData.agency, 'Super-Man delivery');
await page.waitFor(1000);
let emptyZone = await page
.expectPropertyValue(selectors.ticketBasicData.zone, 'value', '');
@ -59,6 +59,7 @@ describe('Ticket Edit basic data path', () => {
});
it(`should edit the ticket zone then check the agency is for the new zone`, async() => {
await page.clearInput(selectors.ticketBasicData.agency);
await page.autocompleteSearch(selectors.ticketBasicData.zone, 'Zone expensive A');
let zone = await page
.waitToGetProperty(selectors.ticketBasicData.agency, 'value');

View File

@ -248,7 +248,8 @@ export default class Autocomplete extends Field {
'where',
'order',
'limit',
'searchFunction'
'searchFunction',
'whereFunction'
]);
}
@ -290,6 +291,7 @@ ngModule.vnComponent('vnAutocomplete', {
limit: '<?',
translateFields: '<?',
searchFunction: '&?',
whereFunction: '&?',
fetchFunction: '<?'
},
transclude: {

View File

@ -55,7 +55,7 @@
}
&.event .day-number {
background-color: $color-main;
color: $color-font-dark;
color: $color-font-bg;
}
& > .day-number {
display: flex;

View File

@ -409,6 +409,9 @@ export default class DropDown extends Popover {
? null
: this.searchFunction({$search: this._search});
if (this.whereFunction)
this.where = this.whereFunction();
Object.assign(filter, {
fields: this.getFields(),
include: this.include,

View File

@ -34,5 +34,8 @@
},
"UserSync": {
"dataSource": "vn"
},
"Mail": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,35 @@
{
"name": "Mail",
"base": "VnModel",
"options": {
"mysql": {
"table": "mail"
}
},
"properties": {
"id": {
"id": true,
"type": "Number"
},
"sender": {
"type": "String"
},
"replyTo": {
"type": "String"
},
"subject": {
"type": "String"
},
"body": {
"type": "String"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -24,7 +24,7 @@ describe('client consumption() filter', () => {
where: {
clientFk: 101
},
order: 'itemTypeFk, itemName, itemSize'
order: 'itemFk'
};
const result = await app.models.Client.consumption(ctx, filter);

View File

@ -3,7 +3,7 @@
url="Orders/filter"
limit="20"
data="orders"
order="landed DESC, clientFk">
order="landed DESC, clientFk, id DESC">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar

View File

@ -29,6 +29,6 @@ describe('ticket editableStates()', () => {
let result = await app.models.State.editableStates(ctx, filter);
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED');
expect(pickerDesignedState).toBeFalsy();
expect(pickerDesignedState).toBeTruthy();
});
});

View File

@ -30,7 +30,7 @@ describe('state isEditable()', () => {
it('should return false if the state is not editable for the given role', async() => {
const employeeRole = 1;
const asignedState = 20;
const asignedState = 13;
let ctx = {req: {accessToken: {userId: employeeRole}}};
let result = await app.models.State.isEditable(ctx, asignedState);

View File

@ -66,7 +66,7 @@ describe('ticket-request filter()', () => {
it('should return the ticket request matching the warehouse ID', async() => {
let ctx = {req: {accessToken: {userId: 9}}, args: {warehouse: 1}};
let result = await app.models.TicketRequest.filter(ctx);
let result = await app.models.TicketRequest.filter(ctx, {order: 'id'});
let requestId = result[0].id;
expect(requestId).toEqual(3);

View File

@ -161,8 +161,13 @@ module.exports = Self => {
if (value) {
return {and: [
{'st.alertLevel': 0},
{'st.code': {neq: 'OK'}},
{'st.code': {neq: 'BOARDING'}}
{'st.code': {nin: [
'OK',
'BOARDING',
'PRINTED',
'PRINTED_AUTO',
'PICKER_DESIGNED'
]}}
]};
} else {
return {and: [

View File

@ -7,8 +7,10 @@
<form name="form">
<vn-card class="vn-w-md vn-pa-lg">
<vn-horizontal>
<vn-autocomplete vn-one
<vn-autocomplete
vn-one
vn-id="client"
required="true"
url="Clients"
label="Client"
show-field="name"
@ -18,7 +20,9 @@
initial-data="$ctrl.clientId"
order="id">
</vn-autocomplete>
<vn-autocomplete vn-one
<vn-autocomplete
vn-one
required="true"
data="$ctrl.addresses"
label="Address"
show-field="nickname"
@ -41,6 +45,7 @@
</append>
</vn-autocomplete>
<vn-autocomplete vn-one
required="true"
url="Warehouses"
label="Warehouse"
show-field="name"
@ -51,6 +56,7 @@
<vn-horizontal>
<vn-autocomplete
vn-one
required="true"
url="Companies"
label="Company"
show-field="code"
@ -60,20 +66,24 @@
</vn-autocomplete>
<vn-autocomplete
vn-one
url="AgencyModes/isActive"
vn-id="agencyMode"
url="AgencyModes/byWarehouse"
label="Agency"
show-field="name"
value-field="id"
ng-model="$ctrl.agencyModeId">
ng-model="$ctrl.agencyModeId"
where-function="$ctrl.agencyModeWhere()">
</vn-autocomplete>
<vn-autocomplete
vn-one
data="zones"
required="true"
url="Zones/includingExpired"
label="Zone"
show-field="name"
value-field="id"
ng-model="$ctrl.zoneId"
vn-acl="productionBoss">
vn-acl="productionBoss"
where-function="$ctrl.zoneWhere()">
<tpl-item>
<span>{{::name}} - Max. {{::hour | date: 'HH:mm'}} h.</span>
</tpl-item>
@ -82,16 +92,19 @@
<vn-horizontal>
<vn-date-picker
vn-one
required="true"
label="Shipped"
ng-model="$ctrl.shipped">
</vn-date-picker>
<vn-input-time
vn-one
required="true"
label="Shipped hour"
ng-model="$ctrl.shipped">
</vn-input-time>
<vn-date-picker
vn-one
required="true"
label="Landed"
ng-model="$ctrl.landed">
</vn-date-picker>

View File

@ -56,12 +56,8 @@ class Controller extends Component {
set warehouseId(value) {
if (value != this.ticket.warehouseFk) {
this.ticket.warehouseFk = value;
this.getShipped({
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: value
});
this.ticket.agencyModeFk = null;
this.ticket.zoneFk = null;
}
}
@ -75,8 +71,7 @@ class Controller extends Component {
shipped: value,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: this.ticket.warehouseFk,
showExpiredZones: false
warehouseFk: this.ticket.warehouseFk
});
}
@ -101,12 +96,17 @@ class Controller extends Component {
set agencyModeId(value) {
if (value != this.ticket.agencyModeFk) {
this.ticket.agencyModeFk = value;
if (!value) return;
const agencyMode = this.$.agencyMode.selection;
this.ticket.warehouseFk = agencyMode.warehouseFk;
this.getLanded({
shipped: this.ticket.shipped,
addressFk: this.ticket.addressFk,
agencyModeFk: value,
warehouseFk: this.ticket.warehouseFk,
showExpiredZones: false
warehouseFk: this.ticket.warehouseFk
});
}
}
@ -241,6 +241,27 @@ class Controller extends Component {
|| !this.ticket.companyFk || !this.ticket.shipped || !this.ticket.landed
|| !this.ticket.zoneFk;
}
zoneWhere() {
if (this.ticket.agencyModeFk) {
return {
shipped: this.ticket.shipped,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: this.ticket.warehouseFk
};
}
return {};
}
agencyModeWhere() {
if (this.warehouseId) {
return {
warehouseFk: this.warehouseId
};
}
return {};
}
}
ngModule.vnComponent('vnTicketBasicDataStepOne', {

View File

@ -94,16 +94,7 @@ describe('Ticket', () => {
jest.spyOn(controller, 'getShipped');
controller.ticket.warehouseId = 1;
controller.warehouseId = 2;
const landed = new Date();
const expectedResult = {
landed: landed,
addressFk: 121,
agencyModeFk: 7,
warehouseFk: 2
};
controller.landed = landed;
expect(controller.getShipped).toHaveBeenCalledWith(expectedResult);
expect(controller.ticket.warehouseFk).toEqual(2);
});
});
@ -125,7 +116,6 @@ describe('Ticket', () => {
shipped: shipped,
addressFk: 121,
agencyModeFk: 7,
showExpiredZones: false,
warehouseFk: 1
};
controller.shipped = shipped;
@ -170,14 +160,14 @@ describe('Ticket', () => {
describe('agencyModeId() setter', () => {
it('should set agencyModeId property and call getLanded() method', () => {
jest.spyOn(controller, 'getLanded');
controller.$.agencyMode = {selection: {warehouseFk: 1}};
const shipped = new Date();
const agencyModeId = 8;
const expectedResult = {
shipped: shipped,
addressFk: 121,
agencyModeFk: agencyModeId,
warehouseFk: 1,
showExpiredZones: false,
warehouseFk: 1
};
controller.ticket.shipped = shipped;
controller.agencyModeId = 8;
@ -363,5 +353,31 @@ describe('Ticket', () => {
expect(controller.landed).toEqual(landed);
});
});
describe('zoneWhere() getter', () => {
it('should return an object containing filter properties', async() => {
const shipped = new Date();
controller.ticket.shipped = shipped;
const expectedResult = {
addressFk: 121,
agencyModeFk: 7,
shipped: shipped,
warehouseFk: 1
};
const result = controller.zoneWhere();
expect(result).toEqual(expect.objectContaining(expectedResult));
});
});
describe('agencyModeWhere() getter', () => {
it('should return an object containing the warehouseFk property', async() => {
const expectedResult = {warehouseFk: 1};
const result = controller.agencyModeWhere();
expect(result).toEqual(expect.objectContaining(expectedResult));
});
});
});
});

View File

@ -161,7 +161,8 @@
</div>
<vn-popup vn-id="summary">
<vn-ticket-summary
ticket="$ctrl.selectedTicket">
ticket="$ctrl.selectedTicket"
model="model">
</vn-ticket-summary>
</vn-popup>
<vn-client-descriptor-popover

View File

@ -57,8 +57,12 @@ class Controller extends Section {
this.vnApp.showSuccess(this.$t('Data saved!'));
if (this.card)
this.card.reload();
else
else {
this.getSummary();
// Refresh index model
if (this.model)
this.model.refresh();
}
});
}
@ -78,7 +82,8 @@ ngModule.vnComponent('vnTicketSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
ticket: '<'
ticket: '<',
model: '<?'
},
require: {
card: '?^vnTicketCard'

View File

@ -31,6 +31,9 @@
"userFk": {
"type" : "Number",
"required": true
},
"bossFk": {
"type" : "Number"
}
},
"relations": {

View File

@ -38,7 +38,7 @@
order="DESC">
</vn-autocomplete>
</div>
<div class="vn-pt-md" style="overflow: hidden;">
<div class="input vn-py-md" style="overflow: hidden;">
<vn-chip ng-repeat="absenceType in absenceTypes" ng-class="::{'selectable': $ctrl.isSubordinate}"
ng-click="$ctrl.pick(absenceType)">
<vn-avatar
@ -48,6 +48,13 @@
{{absenceType.name}}
</vn-chip>
</div>
<div class="vn-py-md">
<vn-chip>
<vn-avatar class="festive">
</vn-avatar>
<span translate>Festive</span>
</vn-chip>
</div>
</div>
</vn-side-menu>
<vn-confirm

View File

@ -85,8 +85,16 @@ class Controller extends Section {
this.events = {};
this.calendar = data.calendar;
let addEvent = (day, event) => {
this.events[new Date(day).getTime()] = event;
let addEvent = (day, newEvent) => {
const timestamp = new Date(day).getTime();
const event = this.events[timestamp];
if (event) {
const oldName = event.name;
Object.assign(event, newEvent);
event.name = `${oldName}, ${event.name}`;
} else
this.events[timestamp] = newEvent;
};
if (data.holidays) {
@ -97,7 +105,7 @@ class Controller extends Section {
addEvent(holiday.dated, {
name: holidayName,
color: '#ff0'
className: 'festive'
});
});
}
@ -127,7 +135,12 @@ class Controller extends Section {
let dayNumber = element.firstElementChild;
dayNumber.title = event.name;
dayNumber.style.backgroundColor = event.color;
dayNumber.style.color = 'rgba(0, 0, 0, 0.7)';
if (event.border)
dayNumber.style.border = event.border;
if (event.className)
dayNumber.classList.add(event.className);
}
pick(absenceType) {

View File

@ -95,7 +95,7 @@ describe('Worker', () => {
let events = controller.events;
expect(events[today.getTime()].name).toEqual('Holiday');
expect(events[today.getTime()].name).toEqual('New year, Holiday');
expect(events[tomorrow.getTime()].name).toEqual('Easter');
expect(events[yesterday.getTime()].name).toEqual('Leave');
expect(events[yesterday.getTime()].color).toEqual('#bbb');

View File

@ -1,5 +1,6 @@
Calendar: Calendario
Holidays: Vacaciones
Festive: Festivo
Used: Utilizados
Year: Año
of: de

View File

@ -36,4 +36,17 @@ vn-worker-calendar {
top: 16px;
right: 16px
}
vn-side-menu div > .input {
border-color: rgba(0, 0, 0, 0.3);
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
}
.festive {
background-color:white;
border: 2px solid $color-alert;
width: 24px;
min-width: 24px;
height: 24px
}
}

View File

@ -0,0 +1,38 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethod('byWarehouse', {
description: 'Returns a list of agencies from a warehouse',
accepts: [{
arg: 'filter',
type: 'Object',
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/byWarehouse`,
verb: 'GET'
}
});
Self.byWarehouse = async filter => {
const conn = Self.dataSource.connector;
const where = {isActive: true};
filter = mergeFilters(filter, {where});
let stmt = new ParameterizedSQL(
`SELECT id, name, warehouseFk
FROM (
SELECT DISTINCT am.id, am.name, am.isActive, zw.warehouseFk
FROM zoneWarehouse zw
JOIN zone z ON z.id = zw.zoneFk
JOIN agencyMode am ON am.id = z.agencyModeFk) am`);
stmt.merge(conn.makeSuffix(filter));
return conn.executeStmt(stmt);
};
};

View File

@ -0,0 +1,21 @@
const app = require('vn-loopback/server/server');
describe('AgencyMode byWarehhouse()', () => {
const warehouseId = 1;
it('should return all the agencies', async() => {
const where = {};
const agencies = await app.models.AgencyMode.byWarehouse({where});
expect(agencies.length).toBeGreaterThan(10);
});
it('should return only the agencies for a warehouse', async() => {
const where = {warehouseFk: warehouseId};
const agencies = await app.models.AgencyMode.byWarehouse({where});
const validWarehouse = agencies.every(agency => agency.warehouseFk = warehouseId);
expect(agencies.length).toEqual(6);
expect(validWarehouse).toBeTruthy();
});
});

View File

@ -1,7 +1,7 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('getLanded', {
Self.remoteMethodCtx('getLanded', {
description: 'Returns the first shipped and landed possible for params',
accessType: 'READ',
accepts: [{
@ -23,11 +23,6 @@ module.exports = Self => {
arg: 'warehouseFk',
type: 'number',
required: true
},
{
arg: 'showExpiredZones',
type: 'boolean',
required: true
}],
returns: {
type: 'object',
@ -39,7 +34,13 @@ module.exports = Self => {
}
});
Self.getLanded = async(shipped, addressFk, agencyModeFk, warehouseFk, showExpiredZones) => {
Self.getLanded = async(ctx, shipped, addressFk, agencyModeFk, warehouseFk) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss');
let showExpired = false;
if (isProductionBoss) showExpired = true;
let stmts = [];
stmts.push(new ParameterizedSQL(
`CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [
@ -47,7 +48,7 @@ module.exports = Self => {
addressFk,
agencyModeFk,
warehouseFk,
showExpiredZones
showExpired
]
));

View File

@ -2,13 +2,13 @@ const app = require('vn-loopback/server/server');
describe('agency getLanded()', () => {
it('should return a landing date', async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const shipped = new Date();
shipped.setDate(shipped.getDate() + 1);
const addressFk = 121;
const agencyModeFk = 7;
const warehouseFk = 1;
const showExpiredZones = true;
let result = await app.models.Agency.getLanded(shipped, addressFk, agencyModeFk, warehouseFk, showExpiredZones);
let result = await app.models.Agency.getLanded(ctx, shipped, addressFk, agencyModeFk, warehouseFk);
expect(result.landed).toBeDefined();
});

View File

@ -0,0 +1,71 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethodCtx('includingExpired', {
description: 'Returns a list of agencies from a warehouse',
accepts: [{
arg: 'filter',
type: 'Object',
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/includingExpired`,
verb: 'GET'
}
});
Self.includingExpired = async(ctx, filter) => {
const userId = ctx.req.accessToken.userId;
const conn = Self.dataSource.connector;
const models = Self.app.models;
const where = filter.where;
const stmts = [];
let stmt;
const filterByAvailability = where.shipped && where.addressFk
&& where.agencyModeFk && where.warehouseFk;
if (filterByAvailability) {
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss');
let showExpired = false;
if (isProductionBoss) showExpired = true;
stmt = new ParameterizedSQL(`CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [
where.shipped,
where.addressFk,
where.agencyModeFk,
where.warehouseFk,
showExpired]);
stmts.push(stmt);
}
delete where.shipped;
delete where.addressFk;
delete where.warehouseFk;
stmt = new ParameterizedSQL(
`SELECT id, name, agencyModeFk
FROM vn.zone z`);
if (filterByAvailability)
stmt.merge(`JOIN tmp.zoneGetLanded zgl ON zgl.zoneFk = z.id`);
stmt.merge(conn.makeWhere(filter.where));
let index;
if (stmts.length)
index = stmts.push(stmt) - 1;
else stmts.push(stmt);
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql);
return index ? result[index] : result;
};
};

View File

@ -0,0 +1,40 @@
const app = require('vn-loopback/server/server');
describe('zone includingExpired()', () => {
const inhousePickupId = 1;
const addressId = 101;
const warehouseId = 1;
it('should return an array containing all zones', async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const where = {};
const result = await app.models.Zone.includingExpired(ctx, {where});
expect(result.length).toBeGreaterThan(2);
});
it('should return an array containing zones from the agencyMode "Inhouse pickup"', async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const where = {agencyModeFk: inhousePickupId};
const result = await app.models.Zone.includingExpired(ctx, {where});
const validAgency = result.every(zone => zone.agencyModeFk = inhousePickupId);
expect(result.length).toEqual(3);
expect(validAgency).toBeTruthy();
});
it('should return an array containing available zones', async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const where = {
shipped: new Date(),
addressFk: addressId,
agencyModeFk: inhousePickupId,
warehouseFk: warehouseId
};
const result = await app.models.Zone.includingExpired(ctx, {where});
const firstZone = result[0];
expect(firstZone.name).toEqual('Zone pickup A');
});
});

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/agency-mode/byWarehouse')(Self);
};

View File

@ -5,6 +5,7 @@ module.exports = Self => {
require('../methods/zone/toggleIsIncluded')(Self);
require('../methods/zone/getUpcomingDeliveries')(Self);
require('../methods/zone/deleteZone')(Self);
require('../methods/zone/includingExpired')(Self);
Self.validatesPresenceOf('agencyModeFk', {
message: `Agency cannot be blank`

382
package-lock.json generated
View File

@ -2954,6 +2954,22 @@
}
}
},
"@babel/runtime-corejs3": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
}
}
},
"@babel/template": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz",
@ -4935,11 +4951,6 @@
"@types/yargs": "^13.0.0"
}
},
"@kyleshockey/object-assign-deep": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz",
"integrity": "sha1-hJAPDu/DcnmPR1G1JigwuCCJIuw="
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -6692,6 +6703,7 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@ -7169,9 +7181,9 @@
}
},
"btoa": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.1.2.tgz",
"integrity": "sha1-PkC4FmP4HS3WWWpMtxSo3BbPq+A="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
},
"buffer": {
"version": "4.9.1",
@ -8014,7 +8026,8 @@
"core-js": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==",
"dev": true
},
"core-js-compat": {
"version": "3.6.5",
@ -8034,6 +8047,11 @@
}
}
},
"core-js-pure": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@ -8086,23 +8104,11 @@
}
},
"cross-fetch": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-0.0.8.tgz",
"integrity": "sha1-Ae2U3EB98sAPGAf95wCnz6SKIFw=",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz",
"integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==",
"requires": {
"node-fetch": "1.7.3",
"whatwg-fetch": "2.0.3"
},
"dependencies": {
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
}
"node-fetch": "2.6.1"
}
},
"cross-spawn": {
@ -8331,9 +8337,9 @@
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
},
"deep-extend": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"deep-is": {
"version": "0.1.3",
@ -8810,23 +8816,15 @@
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
},
"encode-3986": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/encode-3986/-/encode-3986-1.0.0.tgz",
"integrity": "sha1-lA1RSY+HQa3hhLda0UObMXwMemA="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
"encoding-japanese": {
"version": "1.0.30",
"resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-1.0.30.tgz",
"integrity": "sha512-bd/DFLAoJetvv7ar/KIpE3CNO8wEuyrt9Xuw6nSMiZ+Vrz/Q21BPsMHvARL2Wz6IKHKXgb+DWZqtRg1vql9cBg=="
},
"end-of-stream": {
"version": "1.4.1",
@ -12321,8 +12319,7 @@
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"helmet": {
"version": "3.21.2",
@ -12522,6 +12519,17 @@
}
}
},
"html-to-text": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-5.1.1.tgz",
"integrity": "sha512-Bci6bD/JIfZSvG4s0gW/9mMKwBRoe/1RWLxUME/d6WUSZCdY7T60bssf/jFf7EYXRyqU4P5xdClVqiYU0/ypdA==",
"requires": {
"he": "^1.2.0",
"htmlparser2": "^3.10.1",
"lodash": "^4.17.11",
"minimist": "^1.2.0"
}
},
"html-webpack-plugin": {
"version": "4.0.0-beta.11",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz",
@ -12757,6 +12765,38 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true
},
"imap": {
"version": "0.8.19",
"resolved": "https://registry.npmjs.org/imap/-/imap-0.8.19.tgz",
"integrity": "sha1-NniHOTSrCc6mukh0HyhNoq9Z2NU=",
"requires": {
"readable-stream": "1.1.x",
"utf7": ">=1.0.2"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}
}
},
"import-fresh": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
@ -13304,31 +13344,11 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
"isomorphic-form-data": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-0.0.1.tgz",
"integrity": "sha1-Am9ifgMrDNhBPsyHVZKLlKRosGI=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz",
"integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==",
"requires": {
"form-data": "^1.0.0-rc3"
},
"dependencies": {
"async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"requires": {
"lodash": "^4.17.11"
}
},
"form-data": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
"integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
"requires": {
"async": "^2.0.1",
"combined-stream": "^1.0.5",
"mime-types": "^2.1.11"
}
}
"form-data": "^2.3.2"
}
},
"isstream": {
@ -18553,6 +18573,32 @@
"type-check": "~0.3.2"
}
},
"libbase64": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz",
"integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew=="
},
"libmime": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.0.0.tgz",
"integrity": "sha512-2Bm96d5ktnE217Ib1FldvUaPAaOst6GtZrsxJCwnJgi9lnsoAKIHyU0sae8rNx6DNYbjdqqh8lv5/b9poD8qOg==",
"requires": {
"encoding-japanese": "1.0.30",
"iconv-lite": "0.6.2",
"libbase64": "1.2.1",
"libqp": "1.1.0"
},
"dependencies": {
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
}
}
},
"liboneandone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/liboneandone/-/liboneandone-1.2.0.tgz",
@ -18562,6 +18608,11 @@
"request": "^2.74.0"
}
},
"libqp": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz",
"integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g="
},
"liftoff": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
@ -18584,6 +18635,14 @@
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
"dev": true
},
"linkify-it": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
"integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
"requires": {
"uc.micro": "^1.0.1"
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
@ -19330,6 +19389,68 @@
"yallist": "^3.0.2"
}
},
"mailparser": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/mailparser/-/mailparser-2.8.0.tgz",
"integrity": "sha512-Ja5H6/I1NKIFdFJF5HdZkaMGCN8F+1lu3870ZmrabkHQshhrPk4OWeEMiF0OVo82+6XJqemJPCg6pqgXaj3B6Q==",
"requires": {
"encoding-japanese": "1.0.30",
"he": "1.2.0",
"html-to-text": "5.1.1",
"iconv-lite": "0.6.2",
"libmime": "5.0.0",
"linkify-it": "3.0.2",
"mailsplit": "5.0.0",
"nodemailer": "6.4.10",
"tlds": "1.207.0"
},
"dependencies": {
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"nodemailer": {
"version": "6.4.10",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.10.tgz",
"integrity": "sha512-j+pS9CURhPgk6r0ENr7dji+As2xZiHSvZeVnzKniLOw1eRAyM/7flP0u65tCnsapV8JFu+t0l/5VeHsCZEeh9g=="
}
}
},
"mailsplit": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.0.0.tgz",
"integrity": "sha512-HeXA0eyCKBtZqbr7uoeb3Nn2L7VV8Vm27x6/YBb0ZiNzRzLoNS2PqRgGYADwh0cBzLYtqddq40bSSirqLO2LGw==",
"requires": {
"libbase64": "1.2.1",
"libmime": "4.2.1",
"libqp": "1.1.0"
},
"dependencies": {
"iconv-lite": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
"integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"libmime": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-4.2.1.tgz",
"integrity": "sha512-09y7zjSc5im1aNsq815zgo4/G3DnIzym3aDOHsGq4Ee5vrX4PdgQRybAsztz9Rv0NhO+J5C0llEUloa3sUmjmA==",
"requires": {
"encoding-japanese": "1.0.30",
"iconv-lite": "0.5.0",
"libbase64": "1.2.1",
"libqp": "1.1.0"
}
}
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -20202,9 +20323,9 @@
"integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-forge": {
"version": "0.8.5",
@ -22028,7 +22149,8 @@
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
},
"regenerator-transform": {
"version": "0.14.5",
@ -24245,57 +24367,67 @@
}
},
"swagger-client": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.9.0.tgz",
"integrity": "sha512-uyCq2xoaAtmE0oIQ0fCfnsDoy/v97ANnAZywtyk4yumBP74xXp4NFlpZaqZJHN9K9dbPzgs3MH98VocZeM0ExQ==",
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.11.0.tgz",
"integrity": "sha512-5cM2S8qDmA0fwpGW71384Bik7MXI5D8XJz6uVHeH3X3gy9hZo8SDc7MeNShnxSLKAGIUHs9gIqjSeCrUftMNPw==",
"requires": {
"@kyleshockey/js-yaml": "^1.0.1",
"@kyleshockey/object-assign-deep": "^0.4.0",
"babel-runtime": "^6.26.0",
"btoa": "1.1.2",
"buffer": "^5.1.0",
"cookie": "^0.3.1",
"cross-fetch": "0.0.8",
"deep-extend": "^0.5.1",
"encode-3986": "^1.0.0",
"fast-json-patch": "^2.0.6",
"isomorphic-form-data": "0.0.1",
"lodash": "^4.16.2",
"qs": "^6.3.0",
"@babel/runtime-corejs3": "^7.11.2",
"btoa": "^1.2.1",
"buffer": "^5.6.0",
"cookie": "~0.4.1",
"cross-fetch": "^3.0.6",
"deep-extend": "~0.6.0",
"fast-json-patch": "^2.2.1",
"isomorphic-form-data": "~2.0.0",
"js-yaml": "^3.14.0",
"lodash": "^4.17.19",
"qs": "^6.9.4",
"querystring-browser": "^1.0.4",
"traverse": "^0.6.6",
"url": "^0.11.0",
"utf8-bytes": "0.0.1",
"utfstring": "^2.0.0"
"traverse": "~0.6.6",
"url": "~0.11.0"
},
"dependencies": {
"@kyleshockey/js-yaml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@kyleshockey/js-yaml/-/js-yaml-1.0.1.tgz",
"integrity": "sha512-coFyIk1LvTscq1cUU4nCCfYwv+cmG4fCP+wgDKgYZjhM4f++YwZy+g0k+1tUqa4GuUpBTEOGH2KUqKFFWdT73g==",
"requires": {
"argparse": "^1.0.7"
}
},
"buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"fast-json-patch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz",
"integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==",
"requires": {
"fast-deep-equal": "^2.0.1"
}
},
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
"qs": {
"version": "6.9.4",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
},
"url": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
@ -24399,9 +24531,9 @@
},
"dependencies": {
"bl": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
@ -24669,6 +24801,11 @@
"setimmediate": "^1.0.4"
}
},
"tlds": {
"version": "1.207.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.207.0.tgz",
"integrity": "sha512-k7d7Q1LqjtAvhtEOs3yN14EabsNO8ZCoY6RESSJDB9lst3bTx3as/m1UuAeCKzYxiyhR1qq72ZPhpSf+qlqiwg=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -24953,6 +25090,11 @@
"is-typedarray": "^1.0.0"
}
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"uglify-js": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
@ -25293,15 +25435,20 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
"dev": true
},
"utf8-bytes": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/utf8-bytes/-/utf8-bytes-0.0.1.tgz",
"integrity": "sha1-EWsCVEjJtQAIHN+/H01sbDfYg30="
"utf7": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz",
"integrity": "sha1-lV9JCq5lO6IguUVqCod2wZk2CZE=",
"requires": {
"semver": "~5.3.0"
},
"utfstring": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/utfstring/-/utfstring-2.0.0.tgz",
"integrity": "sha512-/ugBfyvIoLe9xqkFHio3CxXnpUKQ1p2LfTxPr6QTRj6GiwpHo73YGdh03UmAzDQNOWpNIE0J5nLss00L4xlWgg=="
"dependencies": {
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
}
}
},
"util": {
"version": "0.11.1",
@ -26376,11 +26523,6 @@
"iconv-lite": "0.4.24"
}
},
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
},
"whatwg-mimetype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",

View File

@ -16,6 +16,7 @@
"fs-extra": "^5.0.0",
"helmet": "^3.21.2",
"i18n": "^0.8.4",
"imap": "^0.8.19",
"ldapjs": "^1.0.2",
"loopback": "^3.26.0",
"loopback-boot": "^2.27.1",
@ -24,6 +25,7 @@
"loopback-connector-mysql": "^5.4.3",
"loopback-connector-remote": "^3.4.1",
"loopback-context": "^3.4.0",
"mailparser": "^2.8.0",
"md5": "^2.2.1",
"node-ssh": "^11.0.0",
"object-diff": "0.0.4",