Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4033-route
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Vicent Llopis 2022-10-04 09:10:15 +02:00
commit 68e4f3a8bc
82 changed files with 14844 additions and 14672 deletions

View File

@ -8,7 +8,7 @@ module.exports = Self => {
},
http: {
path: `/notifyIssues`,
verb: 'GET'
verb: 'POST'
}
});

View File

@ -10,7 +10,7 @@ module.exports = Self => {
},
http: {
path: `/sendQueued`,
verb: 'GET'
verb: 'POST'
}
});

View File

@ -2,10 +2,10 @@ const models = require('vn-loopback/server/server').models;
describe('ticket getCollection()', () => {
it('should return a list of collections', async() => {
let ctx = {req: {accessToken: {userId: 1106}}};
let ctx = {req: {accessToken: {userId: 1107}}};
let response = await models.Collection.getCollection(ctx);
expect(response.length).toBeGreaterThan(0);
expect(response[0].collectionFk).toEqual(1);
expect(response[0].collectionFk).toEqual(3);
});
});

View File

@ -12,7 +12,7 @@ module.exports = Self => {
},
http: {
path: `/deleteTrashFiles`,
verb: 'GET'
verb: 'POST'
}
});
@ -53,8 +53,12 @@ module.exports = Self => {
const pathHash = DmsContainer.getHash(dms.id);
const dmsContainer = await DmsContainer.container(pathHash);
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
try {
await fs.unlink(dstFile);
} catch (err) {
continue;
}
const dstFolder = path.join(dmsContainer.client.root, pathHash);
await fs.unlink(dstFile);
try {
await fs.rmdir(dstFolder);
await dms.destroy(myOptions);

View File

@ -12,7 +12,7 @@ module.exports = Self => {
},
http: {
path: `/updateData`,
verb: 'GET'
verb: 'POST'
}
});

View File

@ -0,0 +1,118 @@
const jsdom = require('jsdom');
const mysql = require('mysql');
module.exports = Self => {
Self.remoteMethodCtx('closeTicket', {
description: 'Close tickets without response from the user',
accessType: 'READ',
returns: {
type: 'Object',
root: true
},
http: {
path: `/closeTicket`,
verb: 'POST'
}
});
Self.closeTicket = async ctx => {
const models = Self.app.models;
const config = await models.OsTicketConfig.findOne();
const ostUri = `${config.host}/login.php`;
if (!config.user || !config.password || !config.userDb || !config.passwordDb)
return false;
const con = mysql.createConnection({
host: `${config.hostDb}`,
user: `${config.userDb}`,
password: `${config.passwordDb}`,
port: `${config.portDb}`
});
const sql = `SELECT ot.ticket_id, ot.number
FROM osticket.ost_ticket ot
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T'
JOIN (
SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
FROM osticket.ost_thread_entry ote
WHERE ote.staff_id != 0 AND ote.type = 'R'
GROUP BY ote.thread_id
) sub ON sub.thread_id = ot2.id
WHERE ot.isanswered = 1
AND ots.state = '${config.oldStatus}'
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ${config.day} DAY)`;
let ticketsId = [];
con.connect(err => {
if (err) throw err;
con.query(sql, (err, results) => {
if (err) throw err;
for (const result of results)
ticketsId.push(result.ticket_id);
});
});
await requestToken();
async function requestToken() {
const response = await fetch(ostUri);
const result = response.headers.get('set-cookie');
const [firtHeader] = result.split(' ');
const firtCookie = firtHeader.substring(0, firtHeader.length - 1);
const body = await response.text();
const dom = new jsdom.JSDOM(body);
const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value;
await login(token, firtCookie);
}
async function login(token, firtCookie) {
const data = {
__CSRFToken__: token,
do: 'scplogin',
userid: config.user,
passwd: config.password,
ajax: 1
};
const params = {
method: 'POST',
body: new URLSearchParams(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': firtCookie
}
};
const response = await fetch(ostUri, params);
const result = response.headers.get('set-cookie');
const [firtHeader] = result.split(' ');
const secondCookie = firtHeader.substring(0, firtHeader.length - 1);
await close(token, secondCookie);
}
async function close(token, secondCookie) {
for (const ticketId of ticketsId) {
const ostUri = `${config.host}/ajax.php/tickets/${ticketId}/status`;
const data = {
status_id: config.newStatusId,
comments: config.comment,
undefined: config.action
};
const params = {
method: 'POST',
body: new URLSearchParams(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRFToken': token,
'Cookie': secondCookie
}
};
return fetch(ostUri, params);
}
}
};
};

View File

@ -116,6 +116,9 @@
"OsTicket": {
"dataSource": "osticket"
},
"OsTicketConfig": {
"dataSource": "vn"
},
"Edi": {
"dataSource": "vn"
}

View File

@ -0,0 +1,52 @@
{
"name": "OsTicketConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "osTicketConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"host": {
"type": "string"
},
"user": {
"type": "string"
},
"password": {
"type": "string"
},
"oldStatus": {
"type": "string"
},
"newStatusId": {
"type": "number"
},
"action": {
"type": "string"
},
"day": {
"type": "number"
},
"comment": {
"type": "string"
},
"hostDb": {
"type": "string"
},
"userDb": {
"type": "string"
},
"passwordDb": {
"type": "string"
},
"portDb": {
"type": "number"
}
}
}

3
back/models/osticket.js Normal file
View File

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

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('OsTicket', '*', '*', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('OsTicketConfig', '*', '*', 'ALLOW', 'ROLE', 'it');

View File

@ -0,0 +1,20 @@
CREATE TABLE `vn`.`osTicketConfig` (
`id` int(11) NOT NULL,
`host` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`user` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`password` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`oldStatus` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`newStatusId` int(11) DEFAULT NULL,
`action` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`day` int(11) DEFAULT NULL,
`comment` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`hostDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`userDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`passwordDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
`portDb` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `vn`.`osTicketConfig`(`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `action`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`)
VALUES
(0, 'https://cau.verdnatura.es/scp', NULL, NULL, 'open', 3, 'Cerrar', 60, 'Este CAU se ha cerrado automáticamente', NULL, NULL, NULL, NULL);

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
VALUES ('Sector','*','READ','ALLOW','ROLE','employee');
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
VALUES ('Sector','*','WRITE','ALLOW','ROLE','employee');

File diff suppressed because one or more lines are too long

View File

@ -583,7 +583,6 @@ INSERT INTO `vn`.`expence`(`id`, `name`, `isWithheld`)
(7001000000, 'Mercaderia', 0),
(7050000000, 'Prestacion de servicios', 1);
INSERT INTO `vn`.`invoiceOutExpence`(`id`, `invoiceOutFk`, `amount`, `expenceFk`, `created`)
VALUES
(1, 1, 813.06, 2000000000, util.VN_CURDATE()),
@ -862,25 +861,25 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
('VT', 'Sales');
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`,
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`)
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
VALUES
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
(5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
(6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
(7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
(8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, NULL),
(9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL),
(10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
(11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1, NULL),
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL),
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL),
(16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL),
(71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0, NULL);
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15,3),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL,NULL),
(10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1, NULL,NULL),
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL,NULL),
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
(16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
(71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL);
-- Update the taxClass after insert of the items
UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2
@ -1877,59 +1876,47 @@ INSERT INTO `pbx`.`sip`(`user_id`, `extension`)
(5, 1102),
(9, 1201);
INSERT INTO `postgresql`.`profile`(`profile_id`, `workerFk`, `profile_type_id`)
SELECT w.id, w.id, 1
FROM `vn`.`worker` `w`;
DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT w.id, w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL +1 YEAR)), '-12-25'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`;
INSERT INTO `postgresql`.`business`(`business_id`, `client_id`, `companyCodeFk`, `date_start`, `date_end`, `workerBusiness`, `reasonEndFk`)
SELECT p.profile_id, p.profile_id, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL +1 YEAR)), '-01-25'), CONCAT('E-46-',RPAD(CONCAT(p.profile_id,9),8,p.profile_id)), NULL
FROM `postgresql`.`profile` `p`;
INSERT INTO `vn`.`business`(`id`, `workerFk`, `companyCodeFk`, `started`, `ended`, `workerBusiness`, `reasonEndFk`, `notes`, `departmentFk`, `workerBusinessProfessionalCategoryFk`, `calendarTypeFk`, `isHourlyLabor`, `workerBusinessAgreementFk`, `workcenterFk`)
SELECT * FROM tmp.worker;
INSERT INTO `postgresql`.`business_labour`(`business_id`, `notes`, `department_id`, `professional_category_id`, `incentivo`, `calendar_labour_type_id`, `porhoras`, `labour_agreement_id`, `workcenter_id`)
SELECT b.business_id, NULL, 23, 1, 0, 1, 1, 1, 1
FROM `postgresql`.`business` `b`;
DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT '1111' as 'id', w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-24'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`
WHERE `w`.`id` = 1109;
INSERT INTO `postgresql`.`business` (`client_id`, `companyCodeFk`, `date_start`, `date_end`, `workerBusiness`, `reasonEndFk`)
SELECT p.profile_id, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-24'), CONCAT('E-46-',RPAD(CONCAT(p.profile_id,9),8,p.profile_id)), NULL
FROM `postgresql`.`profile` `p`
WHERE `p`.`profile_id` = 1109;
INSERT INTO `vn`.`business` (`id`, `workerFk`, `companyCodeFk`, `started`, `ended`, `workerBusiness`, `reasonEndFk`, `notes`, `departmentFk`, `workerBusinessProfessionalCategoryFk`, `calendarTypeFk`, `isHourlyLabor`, `workerBusinessAgreementFk`, `workcenterFk`)
SELECT * FROM tmp.worker;
UPDATE `postgresql`.`business`
DROP TEMPORARY TABLE IF EXISTS tmp.worker;
UPDATE `vn`.`business`
SET `payedHolidays`= 8
WHERE `business_id`= 1106;
WHERE `id`= 1106;
INSERT INTO `postgresql`.`business_labour` (`business_id`, `notes`, `department_id`, `professional_category_id`, `incentivo`, `calendar_labour_type_id`, `porhoras`, `labour_agreement_id`, `workcenter_id`)
VALUES
(1111, NULL, 23, 1, 0.0, 1, 1, 1, 1);
UPDATE `vn`.`business` b
SET b.`workerBusinessProfessionalCategoryFk` = 31
WHERE b.`workerFk` = 1110;
UPDATE `postgresql`.`business_labour` bl
JOIN `postgresql`.`business` b ON b.business_id = bl.business_id
JOIN `postgresql`.`profile` pr ON pr.profile_id = b.client_id
SET bl.`professional_category_id` = 31
WHERE pr.`workerFk` = 1110;
UPDATE `postgresql`.`business_labour` bl
SET bl.`department_id` = 43
WHERE business_id IN(18, 19);
INSERT INTO `postgresql`.`media`(`media_id`, `media_type_id`, `value`, `sort`)
VALUES
(1, 10, 600123321, 0),
(2, 10, 700987987, 0);
INSERT INTO `postgresql`.`profile_media`(`profile_media_id`, `profile_id`, `media_id`)
VALUES
(1, 1106, 1),
(2, 1107, 2);
UPDATE `vn`.`business` b
SET b.`departmentFk` = 43
WHERE b.id IN(18, 19);
INSERT INTO `vn`.`workCenterHoliday` (`workCenterFk`, `days`, `year`)
VALUES
('1', '27.5', YEAR(util.VN_CURDATE())),
('5', '22', YEAR(util.VN_CURDATE())),
('1', '24.5', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR))),
('5', '23', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)));
ALTER TABLE `postgresql`.`business_labour_payroll` DROP FOREIGN KEY `business_labour_payroll_cod_categoria`;
('1', '27.5', YEAR(util.VN_CURDATE())),
('5', '22', YEAR(util.VN_CURDATE())),
('1', '24.5', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR))),
('5', '23', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)));
INSERT INTO `vn`.`workerBusinessType` (`id`, `name`, `isFullTime`, `isPermanent`, `hasHolidayEntitlement`)
VALUES
@ -1937,12 +1924,33 @@ INSERT INTO `vn`.`workerBusinessType` (`id`, `name`, `isFullTime`, `isPermanent`
(100, 'INDEFINIDO A TIEMPO COMPLETO', 1, 1, 1),
(109, 'CONVERSION DE TEMPORAL EN INDEFINIDO T.COMPLETO', 1, 1, 1);
INSERT INTO `postgresql`.`business_labour_payroll` (`business_id`, `cod_tarifa`, `cod_categoria`, `cod_contrato`, `importepactado`)
VALUES
(1, 7, 12, 100, 900.50),
(1106, 7, 12, 100, 1263.03),
(1107, 7, 12, 100, 2000),
(1108, 7, 12, 100, 1500);
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 12,
`workerBusinessTypeFk` = 100,
`amount` = 900.50
WHERE b.id = 1;
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 12,
`workerBusinessTypeFk` = 100,
`amount` = 1263.03
WHERE b.id = 1106;
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 12,
`workerBusinessTypeFk` = 100,
`amount` = 2000
WHERE b.id = 1107;
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 12,
`workerBusinessTypeFk` = 100,
`amount` = 1500
WHERE b.id = 1108;
INSERT INTO `vn`.`absenceType` (`id`, `name`, `rgb`, `code`, `holidayEntitlementRate`, `discountRate`)
VALUES
@ -1953,7 +1961,7 @@ INSERT INTO `vn`.`absenceType` (`id`, `name`, `rgb`, `code`, `holidayEntitlement
(20, 'Furlough', '#97B92F', 'furlough', 1, 1),
(21, 'Furlough half day', '#778899', 'halfFurlough', 0.5, 1);
INSERT INTO `postgresql`.`calendar_employee` (`business_id`, `calendar_state_id`, `date`)
INSERT INTO `postgresql`.`calendar_employee` (`businessFk`, `calendar_state_id`, `date`)
VALUES
(1, 6, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
@ -2448,18 +2456,18 @@ INSERT INTO `vn`.`duaInvoiceIn`(`id`, `duaFk`, `invoiceInFk`)
INSERT INTO `vn`.`invoiceInTax` (`invoiceInFk`, `taxableBase`, `expenceFk`, `foreignValue`, `taxTypeSageFk`, `transactionTypeSageFk`)
VALUES
(1, 99.99, '2000000000', null, null, null),
(2, 999.99, '2000000000', null, null, null),
(3, 1000.50, '2000000000', null, null, null),
(4, 0.50, '2000000000', null, null, null),
(5, 150.50, '2000000000', null, null, null),
(1, 252.25, '4751000000', NULL, 7, 61),
(2, 223.17, '6210000567', NULL, 8, 20),
(3, 95.60, '7001000000', NULL, 8, 35),
(4, 446.63, '7001000000', NULL, 6, 61),
(5, 64.23, '6210000567', NULL, 8, 20),
(6, 29.95, '7001000000', NULL, 7, 20),
(7, 58.64, '6210000567', NULL, 8, 20);
(1, 99.99, '2000000000', NULL, NULL, NULL),
(2, 999.99, '2000000000', NULL, NULL, NULL),
(3, 1000.50, '2000000000', NULL, NULL, NULL),
(4, 0.50, '2000000000', NULL, NULL, NULL),
(5, 150.50, '2000000000', NULL, NULL, NULL),
(1, 252.25, '4751000000', NULL, 7, 61),
(2, 223.17, '6210000567', NULL, 8, 20),
(3, 95.60, '7001000000', NULL, 8, 35),
(4, 446.63, '7001000000', NULL, 6, 61),
(5, 64.23, '6210000567', NULL, 8, 20),
(6, 29.95, '7001000000', NULL, 7, 20),
(7, 58.64, '6210000567', NULL, 8, 20);
INSERT INTO `vn`.`invoiceInIntrastat` (`invoiceInFk`, `net`, `intrastatFk`, `amount`, `stems`, `countryFk`)
VALUES
@ -2635,3 +2643,15 @@ INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`,
INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`)
VALUES
(1, 9);
INSERT INTO `vn`.`productionConfig` (`isPreviousPreparationRequired`, `ticketPrintedMax`, `ticketTrolleyMax`, `rookieDays`, `notBuyingMonths`, `id`, `isZoneClosedByExpeditionActivated`, `maxNotReadyCollections`, `minTicketsToCloseZone`, `movingTicketDelRoute`, `defaultZone`, `defautlAgencyMode`, `hasUniqueCollectionTime`, `maxCollectionWithoutUser`, `pendingCollectionsOrder`, `pendingCollectionsAge`)
VALUES
(0, 8, 80, 0, 0, 1, 0, 15, 25, -1, 697, 1328, 0, 1, 8, 6);
INSERT INTO `vn`.`collection` (`id`, `created`, `workerFk`, `stateFk`, `itemPackingTypeFk`, `saleTotalCount`, `salePickedCount`, `trainFk`, `sectorFk`, `wagons`)
VALUES
(3, util.VN_NOW(), 1107, 5, NULL, 0, 0, 1, NULL, NULL);
INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `level`, `wagon`, `smartTagFk`, `usedShelves`, `itemCount`, `liters`)
VALUES
(9, 3, util.VN_NOW(), NULL, 0, NULL, NULL, NULL, NULL);

File diff suppressed because it is too large Load Diff

View File

@ -239,7 +239,7 @@ xdescribe('worker workerTimeControl_check()', () => {
stmts.push('START TRANSACTION');
stmt = new ParameterizedSQL(`INSERT INTO postgresql.calendar_employee(business_id,calendar_state_id,date)
stmt = new ParameterizedSQL(`INSERT INTO postgresql.calendar_employee(businessFk,calendar_state_id,date)
VALUES
(?,1,CURDATE())`, [
workerId
@ -282,7 +282,7 @@ xdescribe('worker workerTimeControl_check()', () => {
stmts.push('START TRANSACTION');
stmt = new ParameterizedSQL(`UPDATE postgresql.business SET date_end=DATE_ADD(CURDATE(), INTERVAL -1 DAY) WHERE business_id=?`, [
stmt = new ParameterizedSQL(`UPDATE vn.business SET ended = DATE_ADD(CURDATE(), INTERVAL -1 DAY) WHERE id = ?`, [
workerId
]);
stmts.push(stmt);

View File

@ -391,7 +391,7 @@ export default {
intrastadCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Intrastat"]',
originCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Origin"]',
buyerCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Buyer"]',
densityCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Density"]',
weightByPieceCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Weight/Piece"]',
saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button'
},
itemFixedPrice: {
@ -1018,9 +1018,9 @@ export default {
save: 'vn-travel-create vn-submit > button'
},
travelExtraCommunity: {
anySearchResult: 'vn-travel-extra-community > vn-data-viewer div > vn-tbody > vn-tr',
firstTravelReference: 'vn-travel-extra-community vn-tbody:nth-child(2) vn-td-editable[name="reference"]',
firstTravelLockedKg: 'vn-travel-extra-community vn-tbody:nth-child(2) vn-td-editable[name="lockedKg"]',
anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]',
firstTravelReference: 'vn-travel-extra-community tbody:nth-child(2) vn-textfield[ng-model="travel.ref"]',
firstTravelLockedKg: 'vn-travel-extra-community tbody:nth-child(2) vn-input-number[ng-model="travel.kg"]',
removeContinentFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(3) > vn-icon > i'
},
travelBasicData: {

View File

@ -31,7 +31,7 @@ describe('Item index path', () => {
await page.waitToClick(selectors.itemsIndex.intrastadCheckbox);
await page.waitToClick(selectors.itemsIndex.originCheckbox);
await page.waitToClick(selectors.itemsIndex.buyerCheckbox);
await page.waitToClick(selectors.itemsIndex.densityCheckbox);
await page.waitToClick(selectors.itemsIndex.weightByPieceCheckbox);
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
const message = await page.waitForSnackbar();
@ -64,7 +64,7 @@ describe('Item index path', () => {
await page.waitToClick(selectors.itemsIndex.intrastadCheckbox);
await page.waitToClick(selectors.itemsIndex.originCheckbox);
await page.waitToClick(selectors.itemsIndex.buyerCheckbox);
await page.waitToClick(selectors.itemsIndex.densityCheckbox);
await page.waitToClick(selectors.itemsIndex.weightByPieceCheckbox);
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
const message = await page.waitForSnackbar();

View File

@ -22,7 +22,7 @@ describe('InvoiceOut manual invoice path', () => {
});
it('should create an invoice from a ticket', async() => {
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTicket, '7');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTicket, '15');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national');
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);

View File

@ -19,18 +19,22 @@ describe('Travel extra community path', () => {
it('should edit the travel reference and the locked kilograms', async() => {
await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter);
await page.waitForSpinnerLoad();
await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
await page.waitForSpinnerLoad();
await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelLockedKg, '1500');
await page.clearInput(selectors.travelExtraCommunity.firstTravelReference);
await page.write(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
await page.clearInput(selectors.travelExtraCommunity.firstTravelLockedKg);
await page.write(selectors.travelExtraCommunity.firstTravelLockedKg, '1500');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the index and confirm the reference and locked kg were edited', async() => {
await page.accessToSection('travel.index');
await page.accessToSection('travel.extraCommunity');
await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter);
await page.waitForTextInElement(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
const reference = await page.getProperty(selectors.travelExtraCommunity.firstTravelReference, 'innerText');
const lockedKg = await page.getProperty(selectors.travelExtraCommunity.firstTravelLockedKg, 'innerText');
const reference = await page.waitToGetProperty(selectors.travelExtraCommunity.firstTravelReference, 'value');
const lockedKg = await page.waitToGetProperty(selectors.travelExtraCommunity.firstTravelLockedKg, 'value');
expect(reference).toContain('edited reference');
expect(lockedKg).toContain(1500);

View File

@ -150,10 +150,12 @@ describe('Account create and basic data path', () => {
describe('Set password', () => {
it('should set the password using the descriptor menu', async() => {
const newPassword = 'quantum.12345';
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.setPassword);
await page.write(selectors.accountDescriptor.newPassword, 'quantum.crypt0graphy');
await page.write(selectors.accountDescriptor.repeatPassword, 'quantum.crypt0graphy');
await page.write(selectors.accountDescriptor.newPassword, newPassword);
await page.write(selectors.accountDescriptor.repeatPassword, newPassword);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();

View File

@ -23,12 +23,15 @@ export default class InputTime extends Field {
let date = null;
let value = this.input.value;
if (this.field && !this.modelDate)
this.modelDate = this.field;
if (value) {
let split = value.split(':').map(i => parseInt(i) || null);
date = this.field instanceof Date
? this.field
: new Date(this.field || null);
date = this.modelDate
? new Date(this.modelDate)
: new Date();
date.setHours(split[0], split[1], 0, 0);
}

View File

@ -130,5 +130,6 @@
"Descanso diario 12h.": "Daily rest 12h.",
"Fichadas impares": "Odd signs",
"Descanso diario 9h.": "Daily rest 9h.",
"Descanso semanal 36h. / 72h.": "Weekly rest 36h. / 72h."
"Descanso semanal 36h. / 72h.": "Weekly rest 36h. / 72h.",
"Password does not meet requirements": "Password does not meet requirements"
}

View File

@ -146,7 +146,7 @@
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDER_ALREADY_CONFIRMED",
"Invalid password": "Invalid password",
"Password does not meet requirements": "Password does not meet requirements",
"Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Role already assigned",
"Invalid role name": "Invalid role name",
"Role name must be written in camelCase": "Role name must be written in camelCase",
@ -208,7 +208,7 @@
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",

View File

@ -402,8 +402,8 @@ module.exports = Self => {
const models = Self.app.models;
const userId = ctx.options.accessToken.userId;
const isManager = await models.Account.hasRole(userId, 'manager', ctx.options);
if (!isManager) {
const isFinancialBoss = await models.Account.hasRole(userId, 'financialBoss', ctx.options);
if (!isFinancialBoss) {
const lastCredit = await models.ClientCredit.findOne({
where: {
clientFk: finalState.id
@ -413,10 +413,10 @@ module.exports = Self => {
const lastAmount = lastCredit && lastCredit.amount;
const lastWorkerId = lastCredit && lastCredit.workerFk;
const lastWorkerIsManager = await models.Account.hasRole(lastWorkerId, 'manager', ctx.options);
const lastWorkerIsFinancialBoss = await models.Account.hasRole(lastWorkerId, 'financialBoss', ctx.options);
if (lastAmount == 0 && lastWorkerIsManager)
throw new UserError(`You can't change the credit set to zero from a manager`);
if (lastAmount == 0 && lastWorkerIsFinancialBoss)
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
const creditLimits = await models.ClientCreditLimit.find({
fields: ['roleFk'],

View File

@ -53,7 +53,7 @@ describe('Client Model', () => {
});
describe('changeCredit()', () => {
it('should fail to change the credit as a salesAssistant set to zero by a manager', async() => {
it('should fail to change the credit as a salesAssistant set to zero by a financialBoss', async() => {
const tx = await models.Client.beginTransaction({});
let error;
@ -62,7 +62,7 @@ describe('Client Model', () => {
const options = {transaction: tx};
const context = {options};
// Set credit to zero by a manager
// Set credit to zero by a financialBoss
const financialBoss = await models.Account.findOne({
where: {name: 'financialBoss'}
}, options);
@ -83,7 +83,7 @@ describe('Client Model', () => {
await tx.rollback();
}
expect(error.message).toEqual(`You can't change the credit set to zero from a manager`);
expect(error.message).toEqual(`You can't change the credit set to zero from a financialBoss`);
});
it('should fail to change to a high credit amount as a salesAssistant', async() => {

View File

@ -52,7 +52,7 @@ module.exports = Self => {
switch (field) {
case 'size':
case 'density':
case 'weightByPiece':
case 'description':
case 'packingOut':
modelName = 'Item';

View File

@ -157,7 +157,7 @@ module.exports = Self => {
i.image,
i.id AS itemFk,
i.size,
i.density,
i.weightByPiece,
it.code,
i.typeFk,
i.family,

View File

@ -37,7 +37,7 @@ describe('Buy editLatestsBuys()', () => {
const options = {transaction: tx};
try {
const filter = {typeFk: 1};
const filter = {'i.typeFk': 1};
const ctx = {
args: {
filter: filter

View File

@ -4,6 +4,7 @@
order="itemFk DESC"
limit="20"
data="$ctrl.buys"
on-data-change="$ctrl.reCheck()"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
@ -67,8 +68,8 @@
<th field="origin">
<span translate>Origin</span>
</th>
<th field="density">
<span translate>Density</span>
<th field="weightByPiece">
<span translate>Weight/Piece</span>
</th>
<th field="isActive">
<span translate>Active</span>
@ -129,6 +130,7 @@
<td>
<vn-check
ng-model="buy.checked"
on-change="$ctrl.saveChecked(buy.id)"
vn-click-stop>
</vn-check>
</td>
@ -181,7 +183,7 @@
{{::buy.intrastat}}
</td>
<td>{{::buy.origin}}</td>
<td>{{::buy.density}}</td>
<td>{{::buy.weightByPiece}}</td>
<td>
<vn-check
disabled="true"

View File

@ -7,6 +7,7 @@ export default class Controller extends Section {
super($element, $);
this.editedColumn;
this.checkAll = false;
this.checkedBuys = [];
this.smartTableOptions = {
activeButtons: {
@ -79,7 +80,7 @@ export default class Controller extends Section {
{field: 'weight', displayName: this.$t('Weight')},
{field: 'description', displayName: this.$t('Description')},
{field: 'size', displayName: this.$t('Size')},
{field: 'density', displayName: this.$t('Density')},
{field: 'weightByPiece', displayName: this.$t('weight/Piece')},
{field: 'packingOut', displayName: this.$t('PackingOut')},
{field: 'landing', displayName: this.$t('Landing')}
];
@ -102,7 +103,7 @@ export default class Controller extends Section {
switch (param) {
case 'id':
case 'size':
case 'density':
case 'weightByPiece':
case 'isActive':
case 'family':
case 'minPrice':
@ -139,6 +140,7 @@ export default class Controller extends Section {
uncheck() {
this.checkAll = false;
this.checkedBuys = [];
}
get totalChecked() {
@ -148,6 +150,23 @@ export default class Controller extends Section {
return this.checked.length;
}
saveChecked(buyId) {
const index = this.checkedBuys.indexOf(buyId);
if (index !== -1)
return this.checkedBuys.splice(index, 1);
return this.checkedBuys.push(buyId);
}
reCheck() {
if (!this.$.model.data) return;
if (!this.checkedBuys.length) return;
this.$.model.data.forEach(buy => {
if (this.checkedBuys.includes(buy.id))
buy.checked = true;
});
}
onEditAccept() {
const rowsToEdit = [];
for (let row of this.checked)

View File

@ -57,5 +57,44 @@ describe('Entry', () => {
expect(result.length).toEqual(0);
});
});
describe('reCheck()', () => {
it(`should recheck buys`, () => {
controller.$.model.data = [
{checked: false, id: 1},
{checked: false, id: 2},
{checked: false, id: 3},
{checked: false, id: 4},
];
controller.checkedBuys = [1, 2];
controller.reCheck();
expect(controller.$.model.data[0].checked).toEqual(true);
expect(controller.$.model.data[1].checked).toEqual(true);
expect(controller.$.model.data[2].checked).toEqual(false);
expect(controller.$.model.data[3].checked).toEqual(false);
});
});
describe('saveChecked()', () => {
it(`should check buy`, () => {
const buyCheck = 3;
controller.checkedBuys = [1, 2];
controller.saveChecked(buyCheck);
expect(controller.checkedBuys[2]).toEqual(buyCheck);
});
it(`should uncheck buy`, () => {
const buyUncheck = 3;
controller.checkedBuys = [1, 2, 3];
controller.saveChecked(buyUncheck);
expect(controller.checkedBuys[2]).toEqual(undefined);
});
});
});
});

View File

@ -12,7 +12,7 @@ module.exports = Self => {
},
http: {
path: `/downloadImages`,
verb: 'GET'
verb: 'POST'
}
});

View File

@ -42,7 +42,6 @@ module.exports = Self => {
origin.itemTag = undefined;
origin.description = undefined;
origin.image = undefined;
origin.comment = undefined;
origin.size = undefined;

View File

@ -160,7 +160,7 @@ module.exports = Self => {
i.subName,
i.isActive,
i.stems,
i.density,
i.weightByPiece,
i.stemMultiplier,
i.typeFk,
i.isFloramondo,

View File

@ -30,11 +30,24 @@ module.exports = Self => {
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
WHERE year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
GROUP BY buyer, family
) sub
ORDER BY percentage DESC`, [date, date], myOptions);
const wastesTotal = await Self.rawSql(`
SELECT *, 100 * dwindle / total AS percentage
FROM (
SELECT buyer,
sum(ws.saleTotal) AS total,
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
WHERE year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
GROUP BY buyer
) sub
ORDER BY percentage DESC`, [date, date], myOptions);
const details = [];
for (let waste of wastes) {
@ -55,6 +68,14 @@ module.exports = Self => {
buyerDetail.lines.push(waste);
}
for (let waste of details) {
let buyerTotal = wastesTotal.find(totals => {
return waste.buyer == totals.buyer;
});
Object.assign(waste, buyerTotal);
}
return details;
};
};

View File

@ -21,7 +21,7 @@ describe('item clone()', () => {
const result = await models.Item.clone(itemFk, options);
expect(result.id).toEqual(nextItemId);
expect(result.image).toBeUndefined();
expect(result.image).toBeDefined();
expect(result.itemTag).toBeUndefined();
expect(result.comment).toBeUndefined();
expect(result.description).toBeUndefined();

View File

@ -12,6 +12,7 @@ describe('Item getWasteByWorker()', () => {
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
expect(anyResult.buyer).toMatch(/(CharlesXavier|HankPym|DavidCharlesHaller)/);
expect(anyResult.total).toBeGreaterThanOrEqual(1000);
expect(anyResult.lines.length).toBeGreaterThanOrEqual(3);
await tx.rollback();

View File

@ -53,9 +53,9 @@
"type": "number",
"description": "Relevancy"
},
"density": {
"weightByPiece": {
"type": "number",
"description": "Density"
"description": "WeightByPiece"
},
"stemMultiplier": {
"type": "number",

View File

@ -124,9 +124,8 @@
<vn-input-number
vn-one
min="0"
step="0.01"
label="Density"
ng-model="$ctrl.item.density"
label="Weight/Piece"
ng-model="$ctrl.item.weightByPiece"
rule>
</vn-input-number>
<vn-autocomplete

View File

@ -45,8 +45,8 @@
<th field="buyerFk">
<span translate>Buyer</span>
</th>
<th field="density">
<span translate>Density</span>
<th field="weightByPiece">
<span translate>Weight/Piece</span>
</th>
<th field="stemMultiplier">
<span translate>Multiplier</span>
@ -117,7 +117,7 @@
{{::item.userName}}
</span>
</td>
<td>{{::item.density}}</td>
<td>{{::item.weightByPiece}}</td>
<td>{{::item.stemMultiplier}}</td>
<td>
<vn-check

View File

@ -87,7 +87,7 @@ class Controller extends Section {
case 'size':
case 'subname':
case 'isActive':
case 'density':
case 'weightByPiece':
case 'stemMultiplier':
case 'stems':
return {[`i.${param}`]: value};

View File

@ -40,7 +40,7 @@ Create: Crear
Client card: Ficha del cliente
Shipped: F. envío
stems: Tallos
Density: Densidad
Weight/Piece: Peso/tallo
Search items by id, name or barcode: Buscar articulos por identificador, nombre o codigo de barras
SalesPerson: Comercial
Concept: Concepto

View File

@ -91,8 +91,8 @@
<vn-label-value label="Relevancy"
value="{{$ctrl.summary.item.relevancy}}">
</vn-label-value>
<vn-label-value label="Density"
value="{{$ctrl.summary.item.density}}">
<vn-label-value label="Weight/Piece"
value="{{$ctrl.summary.item.weightByPiece}}">
</vn-label-value>
<vn-label-value label="Expense"
value="{{$ctrl.summary.item.expense.name}}">

View File

@ -4,39 +4,46 @@
data="details">
</vn-crud-model>
<vn-data-viewer model="model">
<section ng-repeat="detail in details" class="vn-pa-md">
<vn-horizontal class="header">
<h5><span translate>{{detail.buyer}}</span></h5>
<vn-none>
<vn-icon
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}"
class="arrow pointer"
icon="keyboard_arrow_up"
vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.toggleHidePanel(detail)">
</vn-icon>
</vn-none>
</vn-horizontal>
<vn-card ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th class="waste-family">Family</vn-th>
<vn-th number>Percentage</vn-th>
<vn-th number>Dwindle</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="waste in detail.lines" class="clickable vn-tr"
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})">
<vn-td class="waste-family">{{::waste.family}}</vn-td>
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</section>
<vn-card>
<vn-table>
<vn-thead>
<vn-tr class="header">
<vn-th class="waste-family">Buyer</vn-th>
<vn-th class="waste-family">Family</vn-th>
<vn-th number>Percentage</vn-th>
<vn-th number>Dwindle</vn-th>
<vn-th number>Total</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody ng-repeat="detail in details" class="vn-mb-md">
<vn-tr class="header">
<vn-td>{{::detail.buyer}}</vn-td>
<vn-td>{{::detail.family}}</vn-td>
<vn-td number>{{::(detail.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::detail.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::detail.total | currency: 'EUR'}}</vn-td>
<vn-td shrink>
<vn-icon-button
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}"
class="arrow pointer"
icon="keyboard_arrow_up"
vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.toggleHidePanel(detail)">
</vn-icon-button>
</vn-td>
</vn-tr>
<vn-tr ng-repeat="waste in detail.lines" class="clickable vn-tr"
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})"
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}">
<vn-td></vn-td>
<vn-td>{{::waste.family}}</vn-td>
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
<vn-td shrink></vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>

View File

@ -5,20 +5,9 @@ vn-item-waste-index,
vn-item-waste-detail {
.header {
padding: 12px 0 5px 0;
color: gray;
background-color: $color-bg;
font-size: 1.2rem;
border-bottom: $border;
margin-bottom: 10px;
& > vn-none > vn-icon {
@extend %clickable-light;
color: $color-button;
font-size: 1.4rem;
}
vn-none > .arrow {
transition: transform 200ms;
}
}
vn-table vn-th.waste-family,
@ -26,11 +15,12 @@ vn-item-waste-detail {
max-width: 64px;
width: 64px
}
.hidden {
display: none;
}
.header > vn-none > .arrow.hidden {
.arrow.hidden {
display: block;
transform: rotate(180deg);
}

View File

@ -210,6 +210,9 @@
</slot-table>
</smart-table>
</vn-card>
<vn-ticket-descriptor-popover
vn-id="ticketDescriptor">
</vn-ticket-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -50,8 +50,10 @@
},
"scope": {
"where": {
"isActive": true
}
"isActive": {
"neq": false
}
}
},
"acls": [
{

View File

@ -7,5 +7,8 @@
},
"ShelvingLog": {
"dataSource": "vn"
},
"Sector": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,72 @@
{
"name": "Sector",
"base": "VnModel",
"options": {
"mysql": {
"table": "sector"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"description": {
"type": "string",
"required": true
},
"warehouseFk": {
"type": "number",
"required": true
},
"isPreviousPreparedByPacking": {
"type": "boolean",
"required": true
},
"code": {
"type": "string",
"required": false
},
"isPreviousPrepared": {
"type": "boolean",
"required": true
},
"isPackagingArea": {
"type": "boolean",
"required": true
},
"reportFk": {
"type": "number",
"required": false
},
"sonFk": {
"type": "number",
"required": false
},
"isMain": {
"type": "boolean",
"required": true
},
"itemPackingTypeFk": {
"type": "string",
"required": false
},
"workerFk": {
"type": "number",
"required": false
},
"printerFk": {
"type": "number",
"required": false
},
"isHideForPickers": {
"type": "boolean",
"required": true
},
"isReserve": {
"type": "boolean",
"required": true
}
}
}

View File

@ -82,6 +82,9 @@ describe('ticket setDeleted()', () => {
ctx.req.__ = value => {
return value;
};
const [ticketCollectionOld] = await models.Ticket.rawSql(
`SELECT COUNT(*) numberRows
FROM vn.ticketCollection`, [], options);
const ticketId = 23;
await models.Ticket.setDeleted(ctx, ticketId, options);
@ -90,7 +93,7 @@ describe('ticket setDeleted()', () => {
`SELECT COUNT(*) numberRows
FROM vn.ticketCollection`, [], options);
expect(ticketCollection.numberRows).toEqual(3);
expect(ticketCollection.numberRows).toBeLessThan(ticketCollectionOld.numberRows);
await tx.rollback();
} catch (e) {

View File

@ -128,10 +128,10 @@ module.exports = Self => {
w.name AS warehouseInFk,
w.name AS warehouseInName,
SUM(b.stickers) AS stickers,
s.id AS supplierFk,
s.id AS cargoSupplierFk,
s.nickname AS cargoSupplierNickname,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(vc.aerealVolumetricDensity * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000) as DECIMAL(10,0)) as volumeKg
FROM travel t
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
LEFT JOIN entry e ON e.travelFk = t.id
@ -143,7 +143,8 @@ module.exports = Self => {
JOIN warehouse wo ON wo.id = t.warehouseOutFk
JOIN country c ON c.id = wo.countryFk
LEFT JOIN continent cnt ON cnt.id = c.continentFk
JOIN agencyMode am ON am.id = t.agencyModeFk`
JOIN agencyMode am ON am.id = t.agencyModeFk
JOIN vn.volumeConfig vc`
);
stmt.merge(conn.makeWhere(filter.where));
@ -160,19 +161,21 @@ module.exports = Self => {
e.travelFk,
e.ref,
e.loadPriority,
s.id AS supplierFk,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
e.evaNotes,
e.notes,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedkg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
CAST(SUM(b.weight * b.stickers) AS DECIMAL(10,0)) as loadedkg,
CAST(SUM(vc.aerealVolumetricDensity * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000) AS DECIMAL(10,0)) as volumeKg
FROM tmp.travel tr
JOIN entry e ON e.travelFk = tr.id
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk`
JOIN supplier s ON s.id = e.supplierFk
JOIN vn.volumeConfig vc`
);
stmt.merge(conn.makeGroupBy('e.id'));

View File

@ -50,15 +50,15 @@
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Warehouse In"
ng-model="filter.warehouseInFk"
label="Warehouse Out"
ng-model="filter.warehouseOutFk"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse Out"
ng-model="filter.warehouseOutFk"
label="Warehouse In"
ng-model="filter.warehouseInFk"
url="Warehouses"
show-field="name"
value-field="id">

View File

@ -1,6 +1,7 @@
<vn-crud-model
vn-id="model"
url="Travels/extraCommunityFilter"
filter="::$ctrl.filter"
data="travels"
order="shipped ASC, landed ASC, travelFk, loadPriority, agencyModeFk, evaNotes"
limit="20"
@ -18,112 +19,174 @@
model="model">
</vn-searchbar>
</vn-portal>
<vn-data-viewer model="model" class="travel-list">
<vn-card>
<section class="vn-pa-md">
<vn-tool-bar class="vn-mb-md">
<vn-button disabled="!$ctrl.hasDateRange"
icon="picture_as_pdf"
ng-click="$ctrl.showReport()"
vn-tooltip="Open as PDF">
</vn-button>
</vn-tool-bar>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th shrink>Id</vn-th>
<vn-th expand>Supplier</vn-th>
<vn-th expand>Freighter</vn-th>
<vn-th>Reference</vn-th>
<vn-th number>Packages</vn-th>
<vn-th number>Bl. KG</vn-th>
<vn-th number>Phy. KG</vn-th>
<vn-th number>Vol. KG</vn-th>
<vn-th expand translate-attr="{title: 'Warehouse Out'}">
Wh. Out
</vn-th>
<vn-th expand>W. Shipped</vn-th>
<vn-th expand translate-attr="{title: 'Warehouse In'}">
Wh. In
</vn-th>
<vn-th expand>W. Landed</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody ng-repeat="travel in travels" class="vn-mb-md" vn-droppable="$ctrl.onDrop($event)" ng-attr-id="{{::travel.id}}">
<vn-tr class="header">
<vn-td>
<span class="link"
<vn-card class="travel-list scrollable">
<smart-table
model="model"
options="$ctrl.smartTableOptions">
<slot-actions>
<section>
<vn-tool-bar class="vn-mb-md">
<vn-button
disabled="!$ctrl.hasDateRange"
icon="picture_as_pdf"
ng-click="$ctrl.showReport()"
vn-tooltip="Open as PDF">
</vn-button>
</vn-tool-bar>
</section>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th field="id" shrink>
<span translate>Id</span>
</th>
<th field="cargoSupplierFk" expand>
<span translate>Supplier</span>
</th>
<th field="agencyModeFk" expand>
<span translate>Agency</span>
</th>
<th field="ref">
<span translate>Reference</span>
</th>
<th field="stickers" number>
<span translate>Packages</span>
</th>
<th field="kg" number>
<span translate>Bl. KG</span>
</th>
<th field="loadedKg" number>
<span translate>Phy. KG</span>
</th>
<th field="volumeKg" number>
<span translate>Vol. KG</span>
</th>
<th
field="warehouseOutFk"
translate-attr="{title: 'Warehouse Out'}">
<span translate>Wh. Out</span>
</th>
<th field="shipped">
<span translate>W. Shipped</span>
</th>
<th
field="warehouseInFk"
translate-attr="{title: 'Warehouse In'}">
<span translate>Wh. In</span>
</th>
<th field="landed">
<span translate>W. Landed</span>
</th>
</tr>
</thead>
<tbody
ng-repeat="travel in travels"
class="vn-mb-md"
vn-droppable="$ctrl.onDrop($event)"
ng-attr-id="{{::travel.id}}"
vn-stop-click>
<tr
class="header"
vn-anchor="::{
state: 'travel.card.basicData',
params: {id: travel.id}
}">
<td vn-click-stop>
<span
class="link"
ng-click="travelDescriptor.show($event, travel.id)">
{{::travel.id}}
</span>
</vn-td>
<vn-td expand>{{::travel.agencyModeName}}</vn-td>
<vn-td expand>{{::travel.cargoSupplierNickname}}</vn-td>
<vn-td-editable name="reference" expand>
<text>{{travel.ref}}</text>
<field>
<vn-textfield class="dense" vn-focus
ng-model="travel.ref"
on-change="$ctrl.save(travel.id, {ref: value})">
</vn-textfield>
</field>
</vn-td-editable>
<vn-td number>{{::travel.stickers}}</vn-td>
<vn-td-editable name="lockedKg" expand style="text-align: right">
<text number>{{travel.kg}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="travel.kg"
on-change="$ctrl.save(travel.id, {kg: value})"
min="0">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td number>{{::travel.loadedKg}}</vn-td>
<vn-td number>{{::travel.volumeKg}}</vn-td>
<vn-td expand>{{::travel.warehouseOutName}}</vn-td>
<vn-td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td expand>{{::travel.warehouseInName}}</vn-td>
<vn-td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</vn-td>
</vn-tr>
<a href="#" ng-repeat="entry in travel.entries" class="vn-tr" draggable
</td>
<td expand vn-click-stop>
<span
class="link"
ng-click="supplierDescriptor.show($event, travel.cargoSupplierFk)">
{{::travel.cargoSupplierNickname}}
</span>
</td>
<td expand>{{::travel.agencyModeName}}</td>
<td
name="reference"
expand
vn-click-stop>
<vn-textfield
class="dense td-editable"
ng-model="travel.ref"
on-change="$ctrl.save(travel.id, {ref: value})">
</vn-textfield>
</vn-icon>
</td>
<td number>{{::travel.stickers}}</td>
<td
name="lockedKg"
expand
vn-click-stop>
<vn-input-number
number
class="td-editable number"
ng-model="travel.kg"
on-change="$ctrl.save(travel.id, {kg: value})"
min="0">
</vn-input-number>
</td>
<td number>{{::travel.loadedKg}}</td>
<td number>{{::travel.volumeKg}}</td>
<td expand>{{::travel.warehouseOutName}}</td>
<td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</td>
<td expand>{{::travel.warehouseInName}}</td>
<td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</td>
</tr>
<tr
ng-repeat="entry in travel.entries"
draggable
ng-attr-id="{{::entry.id}}"
ng-click="$event.preventDefault()">
<vn-td>
<td>
<span class="link"
ng-click="entryDescriptor.show($event, entry.id)">
{{::entry.id}}
</span>
</vn-td>
<vn-td>{{::entry.supplierName}}</vn-td>
<vn-td></vn-td>
<vn-td expand>{{::entry.ref}}</vn-td>
<vn-td number>{{::entry.stickers}}</vn-td>
<vn-td number></vn-td>
<vn-td number>{{::entry.loadedkg}}</vn-td>
<vn-td number>{{::entry.volumeKg}}</vn-td>
<vn-td>
</td>
<td>
<span
class="link"
ng-click="supplierDescriptor.show($event, entry.supplierFk)">
{{::entry.supplierName}}
</span>
</td>
<td></td>
<td expand>{{::entry.ref}}</td>
<td number>{{::entry.stickers}}</td>
<td number></td>
<td number>{{::entry.loadedkg}}</td>
<td number>{{::entry.volumeKg}}</td>
<td>
<span ng-if="::entry.notes" vn-tooltip="{{::entry.notes}}">
{{::entry.notes}}
</span>
</vn-td>
<vn-td>
</td>
<td>
<span ng-if="::entry.evaNotes" vn-tooltip="{{::entry.evaNotes}}">
{{::entry.evaNotes}}
</span>
</vn-td>
<vn-td></vn-td>
<vn-td></vn-td>
</a>
</vn-tbody>
</vn-table>
</section>
</vn-card>
</vn-data-viewer>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-travel-descriptor-popover
vn-id="travelDescriptor">
</vn-travel-descriptor-popover>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -14,8 +14,15 @@ class Controller extends Section {
draggable.addEventListener('dragend',
event => this.dragEnd(event));
this.draggableElement = 'a[draggable]';
this.droppableElement = 'vn-tbody[vn-droppable]';
draggable.addEventListener('dragover',
event => this.dragOver(event));
draggable.addEventListener('dragenter',
event => this.dragEnter(event));
draggable.addEventListener('dragleave',
event => this.dragLeave(event));
this.draggableElement = 'tr[draggable]';
this.droppableElement = 'tbody[vn-droppable]';
const twoDays = 2;
const shippedFrom = new Date();
@ -32,6 +39,8 @@ class Controller extends Section {
landedTo: landedTo,
continent: 'AM'
};
this.smartTableOptions = {};
}
get hasDateRange() {
@ -44,6 +53,15 @@ class Controller extends Section {
return hasLanded || hasShipped || hasContinent || hasWarehouseOut;
}
onDragInterval() {
if (this.dragClientY > 0 && this.dragClientY < 75)
this.$window.scrollTo(0, this.$window.scrollY - 10);
const maxHeight = window.screen.availHeight - (window.outerHeight - window.innerHeight);
if (this.dragClientY > maxHeight - 75 && this.dragClientY < maxHeight)
this.$window.scrollTo(0, this.$window.scrollY + 10);
}
findDraggable($event) {
const target = $event.target;
const draggable = target.closest(this.draggableElement);
@ -65,6 +83,7 @@ class Controller extends Section {
const id = parseInt(draggable.id);
this.entryId = id;
this.entry = draggable;
this.interval = setInterval(() => this.onDragInterval(), 50);
}
dragEnd($event) {
@ -72,6 +91,8 @@ class Controller extends Section {
draggable.classList.remove('dragging');
this.entryId = null;
this.entry = null;
clearInterval(this.interval);
}
onDrop($event) {
@ -91,6 +112,37 @@ class Controller extends Section {
}
}
undrop() {
if (!this.dropping) return;
this.dropping.classList.remove('dropping');
this.dropping = null;
}
dragOver($event) {
this.dragClientY = $event.clientY;
$event.preventDefault();
}
dragEnter($event) {
let element = this.findDroppable($event);
if (element) this.dropCount++;
if (element != this.dropping) {
this.undrop();
if (element) element.classList.add('dropping');
this.dropping = element;
}
}
dragLeave($event) {
let element = this.findDroppable($event);
if (element) {
this.dropCount--;
if (this.dropCount == 0) this.undrop();
}
}
save(id, data) {
const endpoint = `Travels/${id}`;
this.$http.patch(endpoint, data)

View File

@ -27,7 +27,7 @@ describe('Travel Component vnTravelExtraCommunity', () => {
describe('findDraggable()', () => {
it('should find the draggable element', () => {
const draggable = document.createElement('a');
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
const $event = new Event('dragstart');
@ -43,7 +43,7 @@ describe('Travel Component vnTravelExtraCommunity', () => {
describe('findDroppable()', () => {
it('should find the droppable element', () => {
const droppable = document.createElement('vn-tbody');
const droppable = document.createElement('tbody');
droppable.setAttribute('vn-droppable', true);
const $event = new Event('drop');
@ -60,7 +60,7 @@ describe('Travel Component vnTravelExtraCommunity', () => {
describe('dragStart()', () => {
it(`should add the class "dragging" to the draggable element
and then set the entryId controller property`, () => {
const draggable = document.createElement('a');
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
draggable.setAttribute('id', 3);
@ -80,7 +80,7 @@ describe('Travel Component vnTravelExtraCommunity', () => {
describe('dragEnd()', () => {
it(`should remove the class "dragging" from the draggable element
and then set the entryId controller property to null`, () => {
const draggable = document.createElement('a');
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
draggable.setAttribute('id', 3);
draggable.classList.add('dragging');
@ -100,13 +100,13 @@ describe('Travel Component vnTravelExtraCommunity', () => {
describe('onDrop()', () => {
it('should make an HTTP patch query', () => {
const droppable = document.createElement('vn-tbody');
const droppable = document.createElement('tbody');
droppable.setAttribute('vn-droppable', true);
droppable.setAttribute('id', 1);
jest.spyOn(controller, 'findDroppable').mockReturnValue(droppable);
const oldDroppable = document.createElement('vn-tbody');
const oldDroppable = document.createElement('tbody');
oldDroppable.setAttribute('vn-droppable', true);
const entry = document.createElement('div');
oldDroppable.appendChild(entry);

View File

@ -15,41 +15,44 @@ vn-travel-extra-community {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
vn-td-editable text {
background-color: transparent;
padding: 0;
border: 0;
border-bottom: 1px dashed $color-active;
border-radius: 0;
color: $color-active
}
vn-td-editable:hover text:after {
font-family: 'Material Icons';
content: 'edit';
position: absolute;
right: -15px;
color: $color-spacer
}
vn-table[vn-droppable] {
table[vn-droppable] {
border-radius: 0;
}
a[draggable] {
tr[draggable] {
transition: all .5s;
cursor: move;
overflow: auto;
outline: 0;
height: 65px;
pointer-events: fill;
user-select:all;
}
a[draggable]:hover {
background-color: $color-hover-cd
tr[draggable] *::selection{
background-color: transparent;
}
a[draggable].dragging {
background-color: $color-success-light;
font-weight:bold
tr[draggable]:hover {
background-color: $color-hover-cd;
}
tr[draggable].dragging {
background-color: $color-primary-light;
color: $color-font-light;
font-weight:bold;
}
.td-editable{
input{
font-size: 1.25rem!important;
}
}
.number *{
text-align: right;
}
}

View File

@ -33,7 +33,7 @@ describe('Worker absences()', () => {
const worker = await app.models.WorkerLabour.findById(businessId, null, options);
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_end = ? WHERE business_id = ?`,
`UPDATE vn.business SET ended = ? WHERE id = ?`,
[null, worker.businessFk], options);
const [absences] = await app.models.Calendar.absences(ctx, worker.id, businessId, year, options);
@ -98,7 +98,7 @@ describe('Worker absences()', () => {
startingContract.setDate(1);
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_start = ?, date_end = ? WHERE business_id = ?`,
`UPDATE vn.business SET started = ?, ended = ? WHERE id = ?`,
[startingContract, yearEnd, contract.businessFk], options
);

View File

@ -11,7 +11,7 @@ module.exports = Self => {
},
http: {
path: `/checkInbox`,
verb: 'GET'
verb: 'POST'
}
});
@ -58,8 +58,8 @@ module.exports = Self => {
emailBody = bufferCopy.toUpperCase().trim();
const bodyPositionOK = emailBody.match(/\bOK\b/i);
if (bodyPositionOK != null && (bodyPositionOK.index == 0 || bodyPositionOK.index == 122))
const bodyPositionIndex = (bodyPositionOK.index == 0 || bodyPositionOK.index == 122);
if (bodyPositionOK != null && bodyPositionIndex)
isEmailOk = true;
else
isEmailOk = false;

View File

@ -86,10 +86,9 @@ module.exports = Self => {
const [result] = await Self.rawSql(
`SELECT COUNT(*) halfHolidayCounter
FROM vn.calendar c
JOIN postgresql.business b ON b.business_id = c.businessFk
JOIN postgresql.profile p ON p.profile_id = b.client_id
JOIN vn.business b ON b.id = c.businessFk
WHERE c.dayOffTypeFk = 6
AND p.workerFk = ?
AND b.workerFk = ?
AND c.dated BETWEEN util.firstDayOfYear(?)
AND LAST_DAY(DATE_ADD(?, INTERVAL 12 - MONTH(?) MONTH))`, [id, date, now, now]);

View File

@ -20,9 +20,6 @@
"EducationLevel": {
"dataSource": "vn"
},
"Sector": {
"dataSource": "vn"
},
"WorkCenter": {
"dataSource": "vn"
},

View File

@ -1,20 +0,0 @@
{
"name": "Sector",
"base": "VnModel",
"options": {
"mysql": {
"table": "sector"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"description": {
"type": "string",
"required": true
}
}
}

View File

@ -1,6 +1,10 @@
{
"name": "ZoneExclusion",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"ZoneLog",
"relation": "zone"
},
"options": {
"mysql": {
"table": "zoneExclusion"

View File

@ -91,7 +91,7 @@
</vn-data-viewer>
</vn-side-menu>
<vn-float-button
ng-click="$ctrl.create('weekday')"
ng-click="$ctrl.createInclusion('weekday')"
icon="add"
vn-tooltip="Add event"
vn-bind="+"

View File

@ -1,6 +1,6 @@
{
"name": "salix-back",
"version": "8.6.0",
"version": "8.8.0",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",
@ -22,6 +22,7 @@
"i18n": "^0.8.4",
"image-type": "^4.1.0",
"imap": "^0.8.19",
"jsdom": "^16.7.0",
"jszip": "^3.10.0",
"ldapjs": "^2.2.0",
"loopback": "^3.26.0",
@ -36,7 +37,7 @@
"node-ssh": "^11.0.0",
"object-diff": "0.0.4",
"object.pick": "^1.3.0",
"puppeteer": "^7.1.0",
"puppeteer": "^18.0.5",
"read-chunk": "^3.2.0",
"require-yaml": "0.0.1",
"sharp": "^0.27.1",

View File

@ -29,7 +29,7 @@ module.exports = {
for (let attachment of options.attachments) {
const fileName = attachment.filename;
const filePath = attachment.path;
if (fileName.includes('.png')) return;
if (fileName.includes('.png')) continue;
if (fileName || filePath)
attachments.push(filePath ? filePath : fileName);

View File

@ -3,13 +3,24 @@ const closure = require('./closure');
module.exports = async function(request, response, next) {
try {
const reqArgs = request.query;
if (!reqArgs.to)
throw new Error('The argument to is required');
const reqArgs = request.body;
response.status(200).json({
message: 'Success'
});
let toDate = new Date();
toDate.setDate(toDate.getDate() - 1);
if (reqArgs.to) toDate = reqArgs.to;
const todayMinDate = new Date();
todayMinDate.setHours(0, 0, 0, 0);
const todayMaxDate = new Date();
todayMinDate.setHours(23, 59, 59, 59);
// Prevent closure for current day
if (toDate >= todayMinDate && toDate <= todayMaxDate)
throw new Error('You cannot close tickets for today');
console.log(`Making closure up to ${toDate}...`);
const tickets = await db.rawSql(`
SELECT
@ -36,7 +47,7 @@ module.exports = async function(request, response, next) {
AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
AND util.dayEnd(?)
AND t.refFk IS NULL
GROUP BY t.id`, [reqArgs.to, reqArgs.to]);
GROUP BY t.id`, [toDate, toDate]);
await closure.start(tickets, response.locals);
@ -52,7 +63,11 @@ module.exports = async function(request, response, next) {
AND util.dayEnd(?)
AND al.code NOT IN('DELIVERED','PACKED')
AND t.routeFk
AND z.name LIKE '%MADRID%'`, [reqArgs.to, reqArgs.to]);
AND z.name LIKE '%MADRID%'`, [toDate, toDate]);
response.status(200).json({
message: 'Success'
});
} catch (error) {
next(error);
}

View File

@ -3,7 +3,7 @@ const closure = require('./closure');
module.exports = async function(request, response, next) {
try {
const reqArgs = request.query;
const reqArgs = request.body;
if (!reqArgs.agencyModeId)
throw new Error('The argument agencyModeId is required');
@ -14,10 +14,6 @@ module.exports = async function(request, response, next) {
if (!reqArgs.to)
throw new Error('The argument to is required');
response.status(200).json({
message: 'Success'
});
const agencyIds = reqArgs.agencyModeId.split(',');
const tickets = await db.rawSql(`
SELECT
@ -53,6 +49,10 @@ module.exports = async function(request, response, next) {
]);
await closure.start(tickets, response.locals);
response.status(200).json({
message: 'Success'
});
} catch (error) {
next(error);
}

View File

@ -4,15 +4,11 @@ const closure = require('./closure');
module.exports = async function(request, response, next) {
try {
const reqArgs = request.query;
const reqArgs = request.body;
if (!reqArgs.routeId)
throw new Error('The argument routeId is required');
response.status(200).json({
message: 'Success'
});
const tickets = await db.rawSql(`
SELECT
t.id,
@ -56,6 +52,10 @@ module.exports = async function(request, response, next) {
const email = new Email('driver-route', args);
await email.send();
}
response.status(200).json({
message: 'Success'
});
} catch (error) {
next(error);
}

View File

@ -3,15 +3,11 @@ const closure = require('./closure');
module.exports = async function(request, response, next) {
try {
const reqArgs = request.query;
const reqArgs = request.body;
if (!reqArgs.ticketId)
throw new Error('The argument ticketId is required');
response.status(200).json({
message: 'Success'
});
const tickets = await db.rawSql(`
SELECT
t.id,
@ -38,6 +34,10 @@ module.exports = async function(request, response, next) {
GROUP BY e.ticketFk`, [reqArgs.ticketId]);
await closure.start(tickets, response.locals);
response.status(200).json({
message: 'Success'
});
} catch (error) {
next(error);
}

View File

@ -7,11 +7,13 @@ const storage = require('vn-print/core/storage');
module.exports = {
async start(tickets, reqArgs) {
console.log(tickets);
if (tickets.length == 0) return;
const failedtickets = [];
for (const ticket of tickets) {
try {
console.log(`Closing ticket id ${ticket.id}...`);
await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]);
const invoiceOut = await db.findOne(`

View File

@ -1,9 +1,9 @@
const express = require('express');
const router = new express.Router();
router.get('/all', require('./closeAll'));
router.get('/by-ticket', require('./closeByTicket'));
router.get('/by-agency', require('./closeByAgency'));
router.get('/by-route', require('./closeByRoute'));
router.post('/all', require('./closeAll'));
router.post('/by-ticket', require('./closeByTicket'));
router.post('/by-agency', require('./closeByAgency'));
router.post('/by-route', require('./closeByRoute'));
module.exports = router;

View File

@ -1,17 +1,18 @@
SELECT
e.id,
e.travelFk,
e.ref,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM travel t
JOIN entry e ON e.travelFk = t.id
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk
WHERE t.id IN(?)
GROUP BY e.id
e.id,
e.travelFk,
e.ref,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(vc.aerealVolumetricDensity * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000) as DECIMAL(10,0)) as volumeKg
FROM travel t
JOIN entry e ON e.travelFk = t.id
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk
JOIN vn.volumeConfig vc
WHERE t.id IN(?)
GROUP BY e.id

View File

@ -6,9 +6,10 @@ SELECT
t.kg,
am.id AS agencyModeFk,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(vc.aerealVolumetricDensity * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000) as DECIMAL(10,0)) as volumeKg
FROM travel t
JOIN volumeConfig vc
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
LEFT JOIN entry e ON e.travelFk = t.id
LEFT JOIN buy b ON b.entryFk = e.id

View File

@ -0,0 +1,9 @@
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new Stylesheet([
`${appPath}/common/css/spacing.css`,
`${appPath}/common/css/misc.css`,
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,19 @@
.column-oriented {
margin-top: 50px !important;
}
.bottom-line > tr {
border-bottom: 1px solid #ccc;
}
.bottom-line tr:nth-last-child() {
border-bottom: none;
}
.report-info {
font-size: 20px
}
.description strong {
text-transform: uppercase;
}

View File

@ -0,0 +1,4 @@
title: Expiración Tarjetas Vehículos
Plate: Matrícula
Concept: Concepto
expirationDate: Fecha caducidad

View File

@ -0,0 +1,7 @@
SELECT
v.numberPlate,
ve.description,
ve.finished
FROM vehicleEvent ve
JOIN vehicle v ON v.id = ve.vehicleFk
WHERE ve.id IN (?)

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Header block -->
<report-header v-bind="$props"></report-header>
<!-- Block -->
<div class="grid-row">
<div class="grid-block">
<div class="content">
<h1 class="title centered uppercase">{{$t('title')}}</h1>
</div>
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('Plate')}}</th>
<th>{{$t('Concept')}}</th>
<th>{{$t('expirationDate')}}</th>
</tr>
</thead>
<tbody v-for="vehicleEvent in vehicleEvents">
<tr>
<td>{{vehicleEvent.numberPlate}}</td>
<td>{{vehicleEvent.description}}</td>
<td>{{vehicleEvent.finished | date('%d-%m-%Y')}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,28 @@
const Component = require(`${appPath}/core/component`);
const reportHeader = new Component('report-header');
const reportFooter = new Component('report-footer');
module.exports = {
name: 'vehicle-event-expired',
async serverPrefetch() {
this.vehicleEvents = await this.fetchVehicleEvent(this.eventIds);
if (!this.vehicleEvents)
throw new Error('Something went wrong');
},
methods: {
fetchVehicleEvent(vehicleEventIds) {
return this.rawSqlFromDef('vehicleEvents', [vehicleEventIds]);
},
},
components: {
'report-header': reportHeader.build(),
'report-footer': reportFooter.build()
},
props: {
eventIds: {
type: [Array],
required: true
}
}
};