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 { try {
mail( mail(
to: env.COMMITTER_EMAIL, 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": { "UserLog": {
"dataSource": "vn" "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 MYSQL_ROOT_PASSWORD root
ENV TZ Europe/Madrid ENV TZ Europe/Madrid
@ -7,9 +7,9 @@ ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \ && apt-get install -y --no-install-recommends curl ca-certificates \
&& curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \ && 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 update \
&& apt-get install -y vn-mysql libmysqlclient21 \ && apt-get install -y vn-mariadb \
&& apt-get purge -y --auto-remove curl ca-certificates \ && apt-get purge -y --auto-remove curl ca-certificates \
&& rm -rf /var/lib/apt/lists/* && 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 \ RUN mkdir /mysql-data \
&& chown -R mysql:mysql /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 docker/docker-boot.sh /docker-entrypoint-initdb.d/
COPY changes import/changes
COPY import-changes.sh config.ini import/
ARG STAMP=unknown ARG STAMP=unknown
RUN gosu mysql docker-init.sh mysqld \ 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 USER mysql
ENTRYPOINT ["docker-start.sh"] ENTRYPOINT ["docker-start.sh"]

View File

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

View File

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

View File

@ -2,9 +2,7 @@
innodb_log_file_size = 4M innodb_log_file_size = 4M
innodb_autoextend_increment = 4 innodb_autoextend_increment = 4
innodb_page_size = 8K innodb_page_size = 8K
innodb_default_row_format = COMPACT
log_bin_trust_function_creators = ON log_bin_trust_function_creators = ON
datadir = /mysql-data datadir = /mysql-data
sql_mode = NO_ENGINE_SUBSTITUTION sql_mode = NO_ENGINE_SUBSTITUTION
innodb_temp_data_file_path = /tmp/ibtmp1:12M:autoextend
skip-log-bin 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 `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`; 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`.`itemTaxCountry` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1; ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`zoneGeo` 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), (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), (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), (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), (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, 67350, 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); (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`) INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`)
VALUES VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18), (1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18),
@ -1071,105 +1075,107 @@ INSERT INTO `vn`.`itemBotanical`(`itemFk`, `botanical`, `genusFk`, `specieFk`)
INSERT INTO `vn`.`itemTag`(`id`,`itemFk`,`tagFk`,`value`,`priority`) INSERT INTO `vn`.`itemTag`(`id`,`itemFk`,`tagFk`,`value`,`priority`)
VALUES VALUES
(1 , 1, 56, 'Ranged weapon', 1), (1, 1, 56, 'Ranged weapon', 1),
(2 , 1, 58, 'longbow', 2), (2, 1, 58, 'longbow', 2),
(3 , 1, 27, '2m', 3), (3, 1, 27, '2m', 3),
(4 , 1, 36, 'Stark Industries', 4), (4, 1, 36, 'Stark Industries', 4),
(5 , 1, 1, 'Brown', 5), (5, 1, 1, 'Brown', 5),
(6 , 1, 67, '+1 precission', 6), (6, 1, 67, '+1 precission', 6),
(7 , 1, 23, '1', 7), (7, 1, 23, '1', 7),
(8 , 2, 56, 'Melee weapon', 1), (8, 2, 56, 'Melee weapon', 1),
(9 , 2, 58, 'combat fist', 2), (9, 2, 58, 'combat fist', 2),
(10, 2, 27, '15cm', 3), (10, 2, 27, '15cm', 3),
(11, 2, 36, 'Stark Industries', 4), (11, 2, 36, 'Stark Industries', 4),
(12, 2, 1, 'Silver', 5), (12, 2, 1, 'Silver', 5),
(13, 2, 67, 'Concussion', 6), (13, 2, 67, 'Concussion', 6),
(14, 2, 23, '2', 7), (14, 2, 23, '2', 7),
(15, 3, 56, 'Ranged weapon', 1), (15, 3, 56, 'Ranged weapon', 1),
(16, 3, 58, 'sniper rifle', 2), (16, 3, 58, 'sniper rifle', 2),
(17, 3, 4, '300mm', 3), (17, 3, 4, '300mm', 3),
(18, 3, 36, 'Stark Industries', 4), (18, 3, 36, 'Stark Industries', 4),
(19, 3, 1, 'Green', 5), (19, 3, 1, 'Green', 5),
(20, 3, 67, 'precission', 6), (20, 3, 67, 'precission', 6),
(21, 3, 23, '3', 7), (21, 3, 23, '3', 7),
(22, 4, 56, 'Melee weapon', 1), (22, 4, 56, 'Melee weapon', 1),
(23, 4, 58, 'heavy shield', 2), (23, 4, 58, 'heavy shield', 2),
(24, 4, 4, '1x0.5m', 3), (24, 4, 4, '1x0.5m', 3),
(25, 4, 36, 'Stark Industries', 4), (25, 4, 36, 'Stark Industries', 4),
(26, 4, 1, 'Black', 5), (26, 4, 1, 'Black', 5),
(27, 4, 67, 'containtment', 6), (27, 4, 67, 'containtment', 6),
(28, 4, 23, '4', 7), (28, 4, 23, '4', 7),
(29, 5, 56, 'Ranged weapon', 1), (29, 5, 56, 'Ranged weapon', 1),
(30, 5, 58, 'pistol', 2), (30, 5, 58, 'pistol', 2),
(31, 5, 27, '9mm', 3), (31, 5, 27, '9mm', 3),
(32, 5, 36, 'Stark Industries', 4), (32, 5, 36, 'Stark Industries', 4),
(33, 5, 1, 'Silver', 5), (33, 5, 1, 'Silver', 5),
(34, 5, 67, 'rapid fire', 6), (34, 5, 67, 'rapid fire', 6),
(35, 5, 23, '5', 7), (35, 5, 23, '5', 7),
(36, 6, 56, 'Container', 1), (36, 6, 56, 'Container', 1),
(37, 6, 58, 'ammo box', 2), (37, 6, 58, 'ammo box', 2),
(38, 6, 27, '1m', 3), (38, 6, 27, '1m', 3),
(39, 6, 36, 'Stark Industries', 4), (39, 6, 36, 'Stark Industries', 4),
(40, 6, 1, 'Green', 5), (40, 6, 1, 'Green', 5),
(41, 6, 67, 'supply', 6), (41, 6, 67, 'supply', 6),
(42, 6, 23, '6', 7), (42, 6, 23, '6', 7),
(43, 7, 56, 'Container', 1), (43, 7, 56, 'Container', 1),
(44, 7, 58, 'medical box', 2), (44, 7, 58, 'medical box', 2),
(45, 7, 27, '1m', 3), (45, 7, 27, '1m', 3),
(46, 7, 36, 'Stark Industries', 4), (46, 7, 36, 'Stark Industries', 4),
(47, 7, 1, 'White', 5), (47, 7, 1, 'White', 5),
(48, 7, 67, 'supply', 6), (48, 7, 67, 'supply', 6),
(49, 7, 23, '7', 7), (49, 7, 23, '7', 7),
(50, 8, 56, 'Ranged Reinforced weapon', 1), (50, 8, 56, 'Ranged Reinforced weapon', 1),
(51, 8, 58, '+1 longbow', 2), (51, 8, 58, '+1 longbow', 2),
(52, 8, 27, '2m', 3), (52, 8, 27, '2m', 3),
(53, 8, 36, 'Stark Industries', 4), (53, 8, 36, 'Stark Industries', 4),
(54, 8, 1, 'Brown', 5), (54, 8, 1, 'Brown', 5),
(55, 8, 67, 'precission', 6), (55, 8, 67, 'precission', 6),
(56, 8, 23, '8', 7), (56, 8, 23, '8', 7),
(57, 9, 56, 'Melee Reinforced weapon', 1), (57, 9, 56, 'Melee Reinforced weapon', 1),
(58, 9, 58, 'combat fist', 2), (58, 9, 58, 'combat fist', 2),
(59, 9, 27, '15cm', 3), (59, 9, 27, '15cm', 3),
(60, 9, 36, 'Stark Industries', 4), (60, 9, 36, 'Stark Industries', 4),
(61, 9, 1, 'Silver', 5), (61, 9, 1, 'Silver', 5),
(62, 9, 67, 'Concussion', 6), (62, 9, 67, 'Concussion', 6),
(63, 9, 23, '9', 7), (63, 9, 23, '9', 7),
(64, 10, 56, 'Ranged Reinforced weapon', 1), (64, 10, 56, 'Ranged Reinforced weapon', 1),
(65, 10, 58, 'sniper rifle', 2), (65, 10, 58, 'sniper rifle', 2),
(66, 10, 4, '300mm', 3), (66, 10, 4, '300mm', 3),
(67, 10, 36, 'Stark Industries', 4), (67, 10, 36, 'Stark Industries', 4),
(68, 10, 1, 'Green', 5), (68, 10, 1, 'Green', 5),
(69, 10, 67, 'precission', 6), (69, 10, 67, 'precission', 6),
(70, 10, 23, '10', 7), (70, 10, 23, '10', 7),
(71, 11, 56, 'Melee Reinforced weapon', 1), (71, 11, 56, 'Melee Reinforced weapon', 1),
(72, 11, 58, 'heavy shield', 2), (72, 11, 58, 'heavy shield', 2),
(73, 11, 4, '1x0.5m', 3), (73, 11, 4, '1x0.5m', 3),
(74, 11, 36, 'Stark Industries', 4), (74, 11, 36, 'Stark Industries', 4),
(75, 11, 1, 'Black', 5), (75, 11, 1, 'Black', 5),
(76, 11, 67, 'containtment', 6), (76, 11, 67, 'containtment', 6),
(77, 11, 23, '11', 7), (77, 11, 23, '11', 7),
(78, 12, 56, 'Ranged Reinforced weapon', 1), (78, 12, 56, 'Ranged Reinforced weapon', 1),
(79, 12, 58, 'pistol', 2), (79, 12, 58, 'pistol', 2),
(80, 12, 27, '9mm', 3), (80, 12, 27, '9mm', 3),
(81, 12, 36, 'Stark Industries', 4), (81, 12, 36, 'Stark Industries', 4),
(82, 12, 1, 'Silver', 5), (82, 12, 1, 'Silver', 5),
(83, 12, 67, 'rapid fire', 6), (83, 12, 67, 'rapid fire', 6),
(84, 12, 23, '12', 7), (84, 12, 23, '12', 7),
(85, 13, 56, 'Chest', 1), (85, 13, 56, 'Chest', 1),
(86, 13, 58, 'ammo box', 2), (86, 13, 58, 'ammo box', 2),
(87, 13, 27, '1m', 3), (87, 13, 27, '1m', 3),
(88, 13, 36, 'Stark Industries', 4), (88, 13, 36, 'Stark Industries', 4),
(89, 13, 1, 'Green', 5), (89, 13, 1, 'Green', 5),
(90, 13, 67, 'supply', 6), (90, 13, 67, 'supply', 6),
(91, 13, 23, '13', 7), (91, 13, 23, '13', 7),
(92, 14, 56, 'Chest', 1), (92, 14, 56, 'Chest', 1),
(93, 14, 58, 'medical box', 2), (93, 14, 58, 'medical box', 2),
(94, 14, 27, '1m', 3), (94, 14, 27, '1m', 3),
(95, 14, 36, 'Stark Industries', 4), (95, 14, 36, 'Stark Industries', 4),
(96, 14, 1, 'White', 5), (96, 14, 1, 'White', 5),
(97, 14, 67, 'supply', 6), (97, 14, 67, 'supply', 6),
(98, 14, 23, '1', 7), (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`) INSERT INTO `vn`.`itemTypeTag`(`id`, `itemTypeFk`, `tagFk`, `priority`)
VALUES 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()), (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()); (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 VALUES
(1, 'Porte Agencia'), (1, 'Porte Agencia', 7001000000),
(2, 'Portes Retorno'), (2, 'Portes Retorno', 7001000000),
(3, 'Porte Carry'), (3, 'Porte Carry', 7001000000),
(4, 'Cargo FITOSANITARIO'), (4, 'Cargo FITOSANITARIO', 4751000000),
(5, 'Documentos'); (5, 'Documentos', 2000000000);
INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`, `ticketServiceTypeFk`) INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`, `ticketServiceTypeFk`)
VALUES VALUES

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
// #2257 xdescribe dbtest workerTimeControl_check() // #2261 xdescribe dbtest workerTimeControl_check()
xdescribe('worker workerTimeControl_check()', () => { xdescribe('worker workerTimeControl_check()', () => {
it(`should throw an error if the worker can't sign on that tablet`, async() => { it(`should throw an error if the worker can't sign on that tablet`, async() => {
let stmts = []; 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() => { 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); await page.waitFor(1000);
let emptyZone = await page let emptyZone = await page
.expectPropertyValue(selectors.ticketBasicData.zone, 'value', ''); .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() => { 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'); await page.autocompleteSearch(selectors.ticketBasicData.zone, 'Zone expensive A');
let zone = await page let zone = await page
.waitToGetProperty(selectors.ticketBasicData.agency, 'value'); .waitToGetProperty(selectors.ticketBasicData.agency, 'value');

View File

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

View File

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

View File

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

View File

@ -34,5 +34,8 @@
}, },
"UserSync": { "UserSync": {
"dataSource": "vn" "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: { where: {
clientFk: 101 clientFk: 101
}, },
order: 'itemTypeFk, itemName, itemSize' order: 'itemFk'
}; };
const result = await app.models.Client.consumption(ctx, filter); const result = await app.models.Client.consumption(ctx, filter);

View File

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

View File

@ -29,6 +29,6 @@ describe('ticket editableStates()', () => {
let result = await app.models.State.editableStates(ctx, filter); let result = await app.models.State.editableStates(ctx, filter);
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED'); 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() => { it('should return false if the state is not editable for the given role', async() => {
const employeeRole = 1; const employeeRole = 1;
const asignedState = 20; const asignedState = 13;
let ctx = {req: {accessToken: {userId: employeeRole}}}; let ctx = {req: {accessToken: {userId: employeeRole}}};
let result = await app.models.State.isEditable(ctx, asignedState); 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() => { it('should return the ticket request matching the warehouse ID', async() => {
let ctx = {req: {accessToken: {userId: 9}}, args: {warehouse: 1}}; 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; let requestId = result[0].id;
expect(requestId).toEqual(3); expect(requestId).toEqual(3);

View File

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

View File

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

View File

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

View File

@ -94,16 +94,7 @@ describe('Ticket', () => {
jest.spyOn(controller, 'getShipped'); jest.spyOn(controller, 'getShipped');
controller.ticket.warehouseId = 1; controller.ticket.warehouseId = 1;
controller.warehouseId = 2; 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); expect(controller.ticket.warehouseFk).toEqual(2);
}); });
}); });
@ -125,7 +116,6 @@ describe('Ticket', () => {
shipped: shipped, shipped: shipped,
addressFk: 121, addressFk: 121,
agencyModeFk: 7, agencyModeFk: 7,
showExpiredZones: false,
warehouseFk: 1 warehouseFk: 1
}; };
controller.shipped = shipped; controller.shipped = shipped;
@ -170,14 +160,14 @@ describe('Ticket', () => {
describe('agencyModeId() setter', () => { describe('agencyModeId() setter', () => {
it('should set agencyModeId property and call getLanded() method', () => { it('should set agencyModeId property and call getLanded() method', () => {
jest.spyOn(controller, 'getLanded'); jest.spyOn(controller, 'getLanded');
controller.$.agencyMode = {selection: {warehouseFk: 1}};
const shipped = new Date(); const shipped = new Date();
const agencyModeId = 8; const agencyModeId = 8;
const expectedResult = { const expectedResult = {
shipped: shipped, shipped: shipped,
addressFk: 121, addressFk: 121,
agencyModeFk: agencyModeId, agencyModeFk: agencyModeId,
warehouseFk: 1, warehouseFk: 1
showExpiredZones: false,
}; };
controller.ticket.shipped = shipped; controller.ticket.shipped = shipped;
controller.agencyModeId = 8; controller.agencyModeId = 8;
@ -363,5 +353,31 @@ describe('Ticket', () => {
expect(controller.landed).toEqual(landed); 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> </div>
<vn-popup vn-id="summary"> <vn-popup vn-id="summary">
<vn-ticket-summary <vn-ticket-summary
ticket="$ctrl.selectedTicket"> ticket="$ctrl.selectedTicket"
model="model">
</vn-ticket-summary> </vn-ticket-summary>
</vn-popup> </vn-popup>
<vn-client-descriptor-popover <vn-client-descriptor-popover

View File

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

View File

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

View File

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

View File

@ -85,8 +85,16 @@ class Controller extends Section {
this.events = {}; this.events = {};
this.calendar = data.calendar; this.calendar = data.calendar;
let addEvent = (day, event) => { let addEvent = (day, newEvent) => {
this.events[new Date(day).getTime()] = event; 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) { if (data.holidays) {
@ -97,7 +105,7 @@ class Controller extends Section {
addEvent(holiday.dated, { addEvent(holiday.dated, {
name: holidayName, name: holidayName,
color: '#ff0' className: 'festive'
}); });
}); });
} }
@ -127,7 +135,12 @@ class Controller extends Section {
let dayNumber = element.firstElementChild; let dayNumber = element.firstElementChild;
dayNumber.title = event.name; dayNumber.title = event.name;
dayNumber.style.backgroundColor = event.color; 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) { pick(absenceType) {

View File

@ -95,7 +95,7 @@ describe('Worker', () => {
let events = controller.events; 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[tomorrow.getTime()].name).toEqual('Easter');
expect(events[yesterday.getTime()].name).toEqual('Leave'); expect(events[yesterday.getTime()].name).toEqual('Leave');
expect(events[yesterday.getTime()].color).toEqual('#bbb'); expect(events[yesterday.getTime()].color).toEqual('#bbb');

View File

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

View File

@ -36,4 +36,17 @@ vn-worker-calendar {
top: 16px; top: 16px;
right: 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; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getLanded', { Self.remoteMethodCtx('getLanded', {
description: 'Returns the first shipped and landed possible for params', description: 'Returns the first shipped and landed possible for params',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
@ -23,11 +23,6 @@ module.exports = Self => {
arg: 'warehouseFk', arg: 'warehouseFk',
type: 'number', type: 'number',
required: true required: true
},
{
arg: 'showExpiredZones',
type: 'boolean',
required: true
}], }],
returns: { returns: {
type: 'object', 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 = []; let stmts = [];
stmts.push(new ParameterizedSQL( stmts.push(new ParameterizedSQL(
`CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [ `CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [
@ -47,7 +48,7 @@ module.exports = Self => {
addressFk, addressFk,
agencyModeFk, agencyModeFk,
warehouseFk, warehouseFk,
showExpiredZones showExpired
] ]
)); ));

View File

@ -2,13 +2,13 @@ const app = require('vn-loopback/server/server');
describe('agency getLanded()', () => { describe('agency getLanded()', () => {
it('should return a landing date', async() => { it('should return a landing date', async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const shipped = new Date(); const shipped = new Date();
shipped.setDate(shipped.getDate() + 1); shipped.setDate(shipped.getDate() + 1);
const addressFk = 121; const addressFk = 121;
const agencyModeFk = 7; const agencyModeFk = 7;
const warehouseFk = 1; const warehouseFk = 1;
const showExpiredZones = true; let result = await app.models.Agency.getLanded(ctx, shipped, addressFk, agencyModeFk, warehouseFk);
let result = await app.models.Agency.getLanded(shipped, addressFk, agencyModeFk, warehouseFk, showExpiredZones);
expect(result.landed).toBeDefined(); 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/toggleIsIncluded')(Self);
require('../methods/zone/getUpcomingDeliveries')(Self); require('../methods/zone/getUpcomingDeliveries')(Self);
require('../methods/zone/deleteZone')(Self); require('../methods/zone/deleteZone')(Self);
require('../methods/zone/includingExpired')(Self);
Self.validatesPresenceOf('agencyModeFk', { Self.validatesPresenceOf('agencyModeFk', {
message: `Agency cannot be blank` message: `Agency cannot be blank`

384
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": { "@babel/template": {
"version": "7.6.0", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz",
@ -4935,11 +4951,6 @@
"@types/yargs": "^13.0.0" "@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": { "@sindresorhus/is": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -6692,6 +6703,7 @@
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": { "requires": {
"core-js": "^2.4.0", "core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0" "regenerator-runtime": "^0.11.0"
@ -7169,9 +7181,9 @@
} }
}, },
"btoa": { "btoa": {
"version": "1.1.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.1.2.tgz", "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha1-PkC4FmP4HS3WWWpMtxSo3BbPq+A=" "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
}, },
"buffer": { "buffer": {
"version": "4.9.1", "version": "4.9.1",
@ -8014,7 +8026,8 @@
"core-js": { "core-js": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", "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": { "core-js-compat": {
"version": "3.6.5", "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": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@ -8086,23 +8104,11 @@
} }
}, },
"cross-fetch": { "cross-fetch": {
"version": "0.0.8", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-0.0.8.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz",
"integrity": "sha1-Ae2U3EB98sAPGAf95wCnz6SKIFw=", "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==",
"requires": { "requires": {
"node-fetch": "1.7.3", "node-fetch": "2.6.1"
"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"
}
}
} }
}, },
"cross-spawn": { "cross-spawn": {
@ -8331,9 +8337,9 @@
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
}, },
"deep-extend": { "deep-extend": {
"version": "0.5.1", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==" "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
}, },
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
@ -8810,23 +8816,15 @@
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true "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": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
}, },
"encoding": { "encoding-japanese": {
"version": "0.1.12", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-1.0.30.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "integrity": "sha512-bd/DFLAoJetvv7ar/KIpE3CNO8wEuyrt9Xuw6nSMiZ+Vrz/Q21BPsMHvARL2Wz6IKHKXgb+DWZqtRg1vql9cBg=="
"requires": {
"iconv-lite": "~0.4.13"
}
}, },
"end-of-stream": { "end-of-stream": {
"version": "1.4.1", "version": "1.4.1",
@ -12321,8 +12319,7 @@
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
"dev": true
}, },
"helmet": { "helmet": {
"version": "3.21.2", "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": { "html-webpack-plugin": {
"version": "4.0.0-beta.11", "version": "4.0.0-beta.11",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz",
@ -12757,6 +12765,38 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true "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": { "import-fresh": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
@ -13304,31 +13344,11 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
}, },
"isomorphic-form-data": { "isomorphic-form-data": {
"version": "0.0.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-0.0.1.tgz", "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz",
"integrity": "sha1-Am9ifgMrDNhBPsyHVZKLlKRosGI=", "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==",
"requires": { "requires": {
"form-data": "^1.0.0-rc3" "form-data": "^2.3.2"
},
"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"
}
}
} }
}, },
"isstream": { "isstream": {
@ -18553,6 +18573,32 @@
"type-check": "~0.3.2" "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": { "liboneandone": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/liboneandone/-/liboneandone-1.2.0.tgz", "resolved": "https://registry.npmjs.org/liboneandone/-/liboneandone-1.2.0.tgz",
@ -18562,6 +18608,11 @@
"request": "^2.74.0" "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": { "liftoff": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
@ -18584,6 +18635,14 @@
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
"dev": true "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": { "load-json-file": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
@ -19330,6 +19389,68 @@
"yallist": "^3.0.2" "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": { "make-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -20202,9 +20323,9 @@
"integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==" "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.0", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
}, },
"node-forge": { "node-forge": {
"version": "0.8.5", "version": "0.8.5",
@ -22028,7 +22149,8 @@
"regenerator-runtime": { "regenerator-runtime": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
}, },
"regenerator-transform": { "regenerator-transform": {
"version": "0.14.5", "version": "0.14.5",
@ -24245,57 +24367,67 @@
} }
}, },
"swagger-client": { "swagger-client": {
"version": "3.9.0", "version": "3.11.0",
"resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.9.0.tgz", "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.11.0.tgz",
"integrity": "sha512-uyCq2xoaAtmE0oIQ0fCfnsDoy/v97ANnAZywtyk4yumBP74xXp4NFlpZaqZJHN9K9dbPzgs3MH98VocZeM0ExQ==", "integrity": "sha512-5cM2S8qDmA0fwpGW71384Bik7MXI5D8XJz6uVHeH3X3gy9hZo8SDc7MeNShnxSLKAGIUHs9gIqjSeCrUftMNPw==",
"requires": { "requires": {
"@kyleshockey/js-yaml": "^1.0.1", "@babel/runtime-corejs3": "^7.11.2",
"@kyleshockey/object-assign-deep": "^0.4.0", "btoa": "^1.2.1",
"babel-runtime": "^6.26.0", "buffer": "^5.6.0",
"btoa": "1.1.2", "cookie": "~0.4.1",
"buffer": "^5.1.0", "cross-fetch": "^3.0.6",
"cookie": "^0.3.1", "deep-extend": "~0.6.0",
"cross-fetch": "0.0.8", "fast-json-patch": "^2.2.1",
"deep-extend": "^0.5.1", "isomorphic-form-data": "~2.0.0",
"encode-3986": "^1.0.0", "js-yaml": "^3.14.0",
"fast-json-patch": "^2.0.6", "lodash": "^4.17.19",
"isomorphic-form-data": "0.0.1", "qs": "^6.9.4",
"lodash": "^4.16.2",
"qs": "^6.3.0",
"querystring-browser": "^1.0.4", "querystring-browser": "^1.0.4",
"traverse": "^0.6.6", "traverse": "~0.6.6",
"url": "^0.11.0", "url": "~0.11.0"
"utf8-bytes": "0.0.1",
"utfstring": "^2.0.0"
}, },
"dependencies": { "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": { "buffer": {
"version": "5.2.1", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
"requires": { "requires": {
"base64-js": "^1.0.2", "base64-js": "^1.0.2",
"ieee754": "^1.1.4" "ieee754": "^1.1.4"
} }
}, },
"cookie": { "cookie": {
"version": "0.3.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" "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": { "punycode": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" "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": { "url": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
@ -24399,9 +24531,9 @@
}, },
"dependencies": { "dependencies": {
"bl": { "bl": {
"version": "4.0.2", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
"requires": { "requires": {
"buffer": "^5.5.0", "buffer": "^5.5.0",
"inherits": "^2.0.4", "inherits": "^2.0.4",
@ -24669,6 +24801,11 @@
"setimmediate": "^1.0.4" "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": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -24953,6 +25090,11 @@
"is-typedarray": "^1.0.0" "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": { "uglify-js": {
"version": "3.4.10", "version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", "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==", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
"dev": true "dev": true
}, },
"utf8-bytes": { "utf7": {
"version": "0.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/utf8-bytes/-/utf8-bytes-0.0.1.tgz", "resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz",
"integrity": "sha1-EWsCVEjJtQAIHN+/H01sbDfYg30=" "integrity": "sha1-lV9JCq5lO6IguUVqCod2wZk2CZE=",
}, "requires": {
"utfstring": { "semver": "~5.3.0"
"version": "2.0.0", },
"resolved": "https://registry.npmjs.org/utfstring/-/utfstring-2.0.0.tgz", "dependencies": {
"integrity": "sha512-/ugBfyvIoLe9xqkFHio3CxXnpUKQ1p2LfTxPr6QTRj6GiwpHo73YGdh03UmAzDQNOWpNIE0J5nLss00L4xlWgg==" "semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
}
}
}, },
"util": { "util": {
"version": "0.11.1", "version": "0.11.1",
@ -26376,11 +26523,6 @@
"iconv-lite": "0.4.24" "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": { "whatwg-mimetype": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",

View File

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