Merge branch 'dev' into 5753_232401_db_dump
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alex Moreno 2023-07-03 06:59:42 +00:00
commit d1fb2859be
52 changed files with 288 additions and 547 deletions

View File

@ -5,21 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2328.01] - 2023-07-13
### Added
### Changed
### Fixed
## [2326.01] - 2023-06-29 ## [2326.01] - 2023-06-29
### Added ### Added
- (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas - (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas
- (General -> Históricos) Botón para ver el estado del registro en cada punto - (General -> Históricos) Botón para ver el estado del registro en cada punto
- (General -> Históricos) Al filtar por registro se muestra todo el histórial desde que fue creado - (General -> Históricos) Al filtar por registro se muestra todo el histórial desde que fue creado
- (Tickets -> Índice) Permite enviar varios albaranes a Docuware
### Changed ### Changed
- (General -> Históricos) Los registros se muestran agrupados por usuario y entidad - (General -> Históricos) Los registros se muestran agrupados por usuario y entidad

View File

@ -17,23 +17,22 @@ module.exports = Self => {
Self.renewToken = async function(ctx) { Self.renewToken = async function(ctx) {
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const token = ctx.req.accessToken;
const created = ctx.req.accessToken.created;
const tokenId = ctx.req.accessToken.id;
const now = new Date(); const now = new Date();
const differenceMilliseconds = now - created; const differenceMilliseconds = now - token.created;
const differenceSeconds = Math.floor(differenceMilliseconds / 1000); const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields: ['renewPeriod']}); const fields = ['renewPeriod', 'courtesyTime'];
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields});
if (differenceSeconds <= accessTokenConfig.renewPeriod) if (differenceSeconds < accessTokenConfig.renewPeriod - accessTokenConfig.courtesyTime)
throw new UserError(`The renew period has not been exceeded`); throw new UserError(`The renew period has not been exceeded`, 'periodNotExceeded');
await Self.logout(tokenId); await Self.logout(token.id);
const user = await Self.findById(userId); const user = await Self.findById(token.userId);
const accessToken = await user.createAccessToken(); const accessToken = await user.createAccessToken();
return {token: accessToken.id, created: accessToken.created}; return {id: accessToken.id, ttl: accessToken.ttl};
}; };
}; };

View File

@ -76,6 +76,6 @@ module.exports = Self => {
let loginInfo = Object.assign({password}, userInfo); let loginInfo = Object.assign({password}, userInfo);
token = await Self.login(loginInfo, 'user'); token = await Self.login(loginInfo, 'user');
return {token: token.id, created: token.created}; return {token: token.id, ttl: token.ttl};
}; };
}; };

View File

@ -16,6 +16,10 @@
"type": "number", "type": "number",
"required": true "required": true
}, },
"courtesyTime": {
"type": "number",
"required": true
},
"renewInterval": { "renewInterval": {
"type": "number", "type": "number",
"required": true "required": true

View File

@ -1,10 +1,11 @@
CREATE TABLE `salix`.`accessTokenConfig` ( CREATE TABLE `salix`.`accessTokenConfig` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`renewPeriod` int(10) unsigned DEFAULT NULL, `renewPeriod` int(10) unsigned DEFAULT NULL,
`courtesyTime` int(10) unsigned DEFAULT NULL,
`renewInterval` int(10) unsigned DEFAULT NULL, `renewInterval` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`) INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `courtesyTime`, `renewInterval`)
VALUES VALUES
(1, 21600, 300); (1, 21600, 5, 300);

View File

@ -1,9 +1,9 @@
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
VALUES VALUES
('InvoiceOut','makePdfAndNotify','WRITE','ALLOW','ROLE','invoicing'), ('InvoiceOut','makePdfAndNotify','WRITE','ALLOW','ROLE','invoicing'),
('InvoiceOutConfig','*','READ','ALLOW','ROLE','invoicing'); ('InvoiceOutConfig','*','READ','ALLOW','ROLE','invoicing');
CREATE OR REPLACE TABLE vn.invoiceOutConfig ( CREATE OR REPLACE TABLE `vn`.`invoiceOutConfig` (
id INT UNSIGNED auto_increment NOT NULL, id INT UNSIGNED auto_increment NOT NULL,
parallelism int UNSIGNED DEFAULT 1 NOT NULL, parallelism int UNSIGNED DEFAULT 1 NOT NULL,
PRIMARY KEY (id) PRIMARY KEY (id)

View File

@ -702,12 +702,12 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`) INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
VALUES VALUES
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T2222222', 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)), (3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T3333333', 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)), (4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T4444444', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)), (5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
@ -2575,6 +2575,26 @@ INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
CALL `vn`.`ticket_doRecalc`(); CALL `vn`.`ticket_doRecalc`();
UPDATE `vn`.`ticket`
SET refFk = 'T1111111'
WHERE id IN (1,2);
UPDATE `vn`.`ticket`
SET refFk = 'T2222222'
WHERE id = 3;
UPDATE `vn`.`ticket`
SET refFk = 'T3333333'
WHERE id = 4;
UPDATE `vn`.`ticket`
SET refFk = 'T4444444'
WHERE id = 5;
UPDATE `vn`.`ticket`
SET refFk = 'A1111111'
WHERE id = 6;
INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`) INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
VALUES VALUES
(1, 1, 1), (1, 1, 1),

View File

@ -12548,7 +12548,7 @@ BEGIN
FROM myTicket t FROM myTicket t
WHERE shipped BETWEEN TIMESTAMP(vFrom) AND TIMESTAMP(vTo, '23:59:59'); WHERE shipped BETWEEN TIMESTAMP(vFrom) AND TIMESTAMP(vTo, '23:59:59');
CALL vn.ticketGetTotal; CALL vn.ticketGetTotal(NULL);
SELECT v.id, IFNULL(v.landed, v.shipped) landed, SELECT v.id, IFNULL(v.landed, v.shipped) landed,
v.shipped, v.companyFk, v.nickname, v.shipped, v.companyFk, v.nickname,
@ -47214,7 +47214,7 @@ BEGIN
ENGINE = MEMORY ENGINE = MEMORY
SELECT vTicketId ticketFk; SELECT vTicketId ticketFk;
CALL ticketGetTotal; CALL ticketGetTotal(NULL);
SELECT total INTO vTotal FROM tmp.ticketTotal; SELECT total INTO vTotal FROM tmp.ticketTotal;
@ -58310,6 +58310,13 @@ BEGIN
DECLARE vIsCEESerial BOOL DEFAULT FALSE; DECLARE vIsCEESerial BOOL DEFAULT FALSE;
DECLARE vIsCorrectInvoiceDate BOOL; DECLARE vIsCorrectInvoiceDate BOOL;
DECLARE vMaxShipped DATE; DECLARE vMaxShipped DATE;
DECLARE vDone BOOL;
DECLARE vTicketFk INT;
DECLARE vCursor CURSOR FOR
SELECT id
FROM ticketToInvoice;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE()); SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE());
@ -58395,6 +58402,20 @@ BEGIN
FROM invoiceOut FROM invoiceOut
WHERE id = vNewInvoiceId; WHERE id = vNewInvoiceId;
OPEN vCursor;
l: LOOP
SET vDone = FALSE;
FETCH vCursor INTO vTicketFk;
IF vDone THEN
LEAVE l;
END IF;
CALL ticket_recalc(vTicketFk, vTaxArea);
END LOOP;
CLOSE vCursor;
UPDATE ticket t UPDATE ticket t
JOIN tmp.ticketToInvoice ti ON ti.id = t.id JOIN tmp.ticketToInvoice ti ON ti.id = t.id
SET t.refFk = vNewRef; SET t.refFk = vNewRef;
@ -69320,7 +69341,7 @@ DELIMITER ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ; /*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;; DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `ticketGetTotal`() CREATE DEFINER=`root`@`localhost` PROCEDURE `ticketGetTotal`(vTaxArea VARCHAR(25))
BEGIN BEGIN
/** /**
* Calcula el total con IVA para un conjunto de tickets. * Calcula el total con IVA para un conjunto de tickets.
@ -69328,7 +69349,7 @@ BEGIN
* @table tmp.ticket(ticketFk) Identificadores de los tickets a calcular * @table tmp.ticket(ticketFk) Identificadores de los tickets a calcular
* @return tmp.ticketTotal Total para cada ticket * @return tmp.ticketTotal Total para cada ticket
*/ */
CALL ticket_getTax(NULL); CALL ticket_getTax(vTaxArea);
DROP TEMPORARY TABLE IF EXISTS tmp.ticketTotal; DROP TEMPORARY TABLE IF EXISTS tmp.ticketTotal;
CREATE TEMPORARY TABLE tmp.ticketTotal CREATE TEMPORARY TABLE tmp.ticketTotal
@ -69479,7 +69500,7 @@ BEGIN
AND clientFk = vClientFk AND clientFk = vClientFk
AND shipped > '2001-01-01'; AND shipped > '2001-01-01';
CALL vn.ticketGetTotal; CALL vn.ticketGetTotal(NULL);
SELECT c.id, SELECT c.id,
c.name as Cliente, c.name as Cliente,
@ -71324,7 +71345,7 @@ proc: BEGIN
LEAVE myLoop; LEAVE myLoop;
END IF; END IF;
CALL ticket_recalc(vTicketFk); CALL ticket_recalc(vTicketFk, NULL);
END LOOP; END LOOP;
CLOSE cCur; CLOSE cCur;
@ -71781,15 +71802,15 @@ BEGIN
JOIN ticket t ON t.id = tmpTicket.ticketFk; JOIN ticket t ON t.id = tmpTicket.ticketFk;
CALL addressTaxArea (); CALL addressTaxArea ();
IF vTaxArea > '' THEN IF vTaxArea IS NOT NULL THEN
UPDATE tmp.addressTaxArea UPDATE tmp.addressTaxArea
SET areaFk = vTaxArea; SET areaFk = vTaxArea;
END IF; END IF;
/* Solo se calcula la base imponible (taxableBase) y el impuesto se calculará posteriormente /* Solo se calcula la base imponible (taxableBase) y el impuesto se calculará posteriormente
* No se debería cambiar el sistema por problemas con los decimales * No se debería cambiar el sistema por problemas con los decimales
*/ */
DROP TEMPORARY TABLE IF EXISTS tmp.ticketTax; DROP TEMPORARY TABLE IF EXISTS tmp.ticketTax;
CREATE TEMPORARY TABLE tmp.ticketTax CREATE TEMPORARY TABLE tmp.ticketTax
(PRIMARY KEY (ticketFk, code, rate)) (PRIMARY KEY (ticketFk, code, rate))
@ -71797,7 +71818,7 @@ BEGIN
SELECT * FROM ( SELECT * FROM (
SELECT tmpTicket.ticketFk, SELECT tmpTicket.ticketFk,
bp.pgcFk, bp.pgcFk,
SUM(s.quantity * s.price * (100 - s.discount)/100 ) AS taxableBase, SUM(s.quantity * s.price * (100 - s.discount)/100 ) taxableBase,
pgc.rate, pgc.rate,
tc.code, tc.code,
bp.priority bp.priority
@ -71817,7 +71838,7 @@ BEGIN
JOIN pgc ON pgc.code = bp.pgcFk JOIN pgc ON pgc.code = bp.pgcFk
JOIN taxClass tc ON tc.id = bp.taxClassFk JOIN taxClass tc ON tc.id = bp.taxClassFk
GROUP BY tmpTicket.ticketFk, pgc.code, pgc.rate GROUP BY tmpTicket.ticketFk, pgc.code, pgc.rate
HAVING taxableBase != 0) t3 HAVING taxableBase <> 0) t3
ORDER BY priority; ORDER BY priority;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketServiceTax; DROP TEMPORARY TABLE IF EXISTS tmp.ticketServiceTax;
@ -71826,7 +71847,7 @@ BEGIN
ENGINE = MEMORY ENGINE = MEMORY
SELECT tt.ticketFk, SELECT tt.ticketFk,
pgc.code pgcFk, pgc.code pgcFk,
SUM(ts.quantity * ts.price) AS taxableBase, SUM(ts.quantity * ts.price) taxableBase,
pgc.rate, pgc.rate,
tc.code tc.code
FROM tmp.ticket tt FROM tmp.ticket tt
@ -71842,7 +71863,7 @@ BEGIN
JOIN pgc ON pgc.code = bp.pgcFk JOIN pgc ON pgc.code = bp.pgcFk
JOIN taxClass tc ON tc.id = bp.taxClassFk JOIN taxClass tc ON tc.id = bp.taxClassFk
GROUP BY tt.ticketFk, pgc.code GROUP BY tt.ticketFk, pgc.code
HAVING taxableBase != 0; HAVING taxableBase <> 0;
INSERT INTO tmp.ticketTax (ticketFk, pgcFk, taxableBase, rate, code) INSERT INTO tmp.ticketTax (ticketFk, pgcFk, taxableBase, rate, code)
SELECT ts.ticketFk, ts.pgcFk, ts.taxableBase, ts.rate, ts.code SELECT ts.ticketFk, ts.pgcFk, ts.taxableBase, ts.rate, ts.code
@ -71853,8 +71874,8 @@ BEGIN
CREATE TEMPORARY TABLE tmp.ticketAmount CREATE TEMPORARY TABLE tmp.ticketAmount
(INDEX (ticketFk)) (INDEX (ticketFk))
ENGINE = MEMORY ENGINE = MEMORY
SELECT ticketFk, SELECT ticketFk,
taxableBase, taxableBase,
SUM(CAST(taxableBase * rate / 100 AS DECIMAL(10, 2))) tax, SUM(CAST(taxableBase * rate / 100 AS DECIMAL(10, 2))) tax,
code code
FROM tmp.ticketTax FROM tmp.ticketTax
@ -72141,20 +72162,31 @@ DELIMITER ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ; /*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;; DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `ticket_recalc`(vTicketId INT) CREATE DEFINER=`root`@`localhost` PROCEDURE `ticket_recalc`(vSelf INT, vTaxArea VARCHAR(25))
BEGIN proc:BEGIN
/** /**
* Calcula y guarda el total con/sin IVA en un ticket. * Calcula y guarda el total con/sin IVA en un ticket.
* *
* @param vTicketId Identificador del ticket * @param vTicketId Identificador del ticket
*/ */
DECLARE hasInvoice BOOL;
SELECT COUNT(*) INTO hasInvoice
FROM ticket
WHERE id = vSelf
AND refFk IS NOT NULL;
IF hasInvoice THEN
LEAVE proc;
END IF;
DROP TEMPORARY TABLE IF EXISTS tmp.ticket; DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
CREATE TEMPORARY TABLE tmp.ticket CREATE TEMPORARY TABLE tmp.ticket
ENGINE = MEMORY ENGINE = MEMORY
SELECT vTicketId ticketFk; SELECT vSelf ticketFk;
CALL ticketGetTotal; CALL ticketGetTotal(vTaxArea);
UPDATE ticket t UPDATE ticket t
JOIN tmp.ticketTotal tt ON tt.ticketFk = t.id JOIN tmp.ticketTotal tt ON tt.ticketFk = t.id

View File

@ -39,9 +39,9 @@ describe('Client web access path', () => {
const userName = await page.getValue($.userName); const userName = await page.getValue($.userName);
const email = await page.getValue($.email); const email = await page.getValue($.email);
await page.accessToSection('client.card.log'); // await page.accessToSection('client.card.log');
const logName = await page.innerText($.nameValue); // const logName = await page.innerText($.nameValue);
const logActive = await page.innerText($.activeValue); // const logActive = await page.innerText($.activeValue);
expect(enableMessage.type).toBe('success'); expect(enableMessage.type).toBe('success');
expect(modifyMessage.type).toBe('success'); expect(modifyMessage.type).toBe('success');
@ -50,7 +50,7 @@ describe('Client web access path', () => {
expect(userName).toEqual('Legion'); expect(userName).toEqual('Legion');
expect(email).toEqual('legion@marvel.com'); expect(email).toEqual('legion@marvel.com');
expect(logName).toEqual('Legion'); // expect(logName).toEqual('Legion');
expect(logActive).toEqual('✗'); // expect(logActive).toEqual('✗');
}); });
}); });

View File

@ -9,7 +9,7 @@ describe('Travel descriptor path', () => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('buyer', 'travel'); await page.loginAndModule('buyer', 'travel');
await page.write(selectors.travelIndex.generalSearchFilter, '1'); await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter'); await page.keyboard.press('Enter');
await page.waitForState('travel.card.summary'); await page.waitForState('travel.card.summary');
}); });
@ -23,7 +23,7 @@ describe('Travel descriptor path', () => {
await page.waitForState('travel.index'); await page.waitForState('travel.index');
const result = await page.countElement(selectors.travelIndex.anySearchResult); const result = await page.countElement(selectors.travelIndex.anySearchResult);
expect(result).toBeGreaterThanOrEqual(7); expect(result).toBeGreaterThanOrEqual(1);
}); });
it('should navigate to the first search result', async() => { it('should navigate to the first search result', async() => {

View File

@ -59,12 +59,13 @@ export default class Auth {
password: password || undefined password: password || undefined
}; };
const now = new Date();
return this.$http.post('VnUsers/signIn', params) return this.$http.post('VnUsers/signIn', params)
.then(json => this.onLoginOk(json, remember)); .then(json => this.onLoginOk(json, now, remember));
} }
onLoginOk(json, remember) { onLoginOk(json, now, remember) {
this.vnToken.set(json.data.token, json.data.created, remember); this.vnToken.set(json.data.token, now, json.data.ttl, remember);
return this.loadAcls().then(() => { return this.loadAcls().then(() => {
let continueHash = this.$state.params.continue; let continueHash = this.$state.params.continue;

View File

@ -1,11 +1,16 @@
import ngModule from '../module'; import ngModule from '../module';
import HttpError from 'core/lib/http-error'; import HttpError from 'core/lib/http-error';
interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate']; interceptor.$inject = ['$q', 'vnApp', '$translate'];
function interceptor($q, vnApp, vnToken, $translate) { function interceptor($q, vnApp, $translate) {
let apiPath = 'api/'; let apiPath = 'api/';
let token = sessionStorage.getItem('vnToken')
?? localStorage.getItem('vnToken');
return { return {
setToken(newToken) {
token = newToken;
},
setApiPath(path) { setApiPath(path) {
apiPath = path; apiPath = path;
}, },
@ -14,8 +19,8 @@ function interceptor($q, vnApp, vnToken, $translate) {
if (config.url.charAt(0) !== '/' && apiPath) if (config.url.charAt(0) !== '/' && apiPath)
config.url = `${apiPath}${config.url}`; config.url = `${apiPath}${config.url}`;
if (vnToken.token) if (token)
config.headers.Authorization = vnToken.token; config.headers.Authorization = token;
if ($translate.use()) if ($translate.use())
config.headers['Accept-Language'] = $translate.use(); config.headers['Accept-Language'] = $translate.use();
if (config.filter) { if (config.filter) {

View File

@ -6,37 +6,118 @@ import ngModule from '../module';
* @property {String} token The current login token or %null * @property {String} token The current login token or %null
*/ */
export default class Token { export default class Token {
constructor() { constructor(vnInterceptor, $http, $rootScope) {
try { Object.assign(this, {
this.token = sessionStorage.getItem('vnToken'); vnInterceptor,
this.created = sessionStorage.getItem('vnTokenCreated'); $http,
if (!this.token) { $rootScope
this.token = localStorage.getItem('vnToken'); });
this.created = localStorage.getItem('vnTokenCreated');
}
} catch (e) {}
}
set(token, created, remember) {
this.unset();
try {
if (remember) {
localStorage.setItem('vnToken', token);
localStorage.setItem('vnTokenCreated', created);
} else {
sessionStorage.setItem('vnToken', token);
sessionStorage.setItem('vnTokenCreated', created);
}
} catch (e) {}
this.token = token; try {
this.created = created; this.getStorage(sessionStorage);
this.remember = true;
if (!this.token) {
this.getStorage(localStorage);
this.remember = false;
}
} catch (e) {}
} }
set(token, created, ttl, remember) {
this.unset();
Object.assign(this, {
token,
created,
ttl,
remember
});
this.vnInterceptor.setToken(token);
try {
if (remember)
this.setStorage(localStorage, token, created, ttl);
else
this.setStorage(sessionStorage, token, created, ttl);
} catch (err) {
console.error(err);
}
}
unset() { unset() {
localStorage.removeItem('vnToken');
sessionStorage.removeItem('vnToken');
this.token = null; this.token = null;
this.created = null; this.created = null;
this.ttl = null;
this.remember = null;
this.vnInterceptor.setToken(null);
this.removeStorage(localStorage);
this.removeStorage(sessionStorage);
}
getStorage(storage) {
this.token = storage.getItem('vnToken');
if (!this.token) return;
const created = storage.getItem('vnTokenCreated');
this.created = created && new Date(created);
this.renewPeriod = storage.getItem('vnTokenRenewPeriod');
}
setStorage(storage, token, created, ttl) {
storage.setItem('vnToken', token);
storage.setItem('vnTokenCreated', created.toJSON());
storage.setItem('vnTokenTtl', ttl);
}
removeStorage(storage) {
storage.removeItem('vnToken');
storage.removeItem('vnTokenCreated');
storage.removeItem('vnTokenTtl');
}
fetchConfig() {
const filter = {fields: ['renewInterval', 'renewPeriod']};
this.$http.get('AccessTokenConfigs/findOne', {filter}).then(res => {
const data = res.data;
if (!data) return;
this.renewPeriod = data.renewPeriod;
this.stopRenewer();
this.inservalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
this.checkValidity();
});
}
checkValidity() {
if (this.checking || !this.created) return;
this.checking = true;
const renewPeriod = Math.min(this.ttl, this.renewPeriod) * 1000;
const maxDate = this.created.getTime() + renewPeriod;
const now = new Date();
if (now.getTime() <= maxDate) {
this.checking = false;
return;
}
this.$http.post('VnUsers/renewToken')
.then(res => {
const token = res.data;
this.set(token.id, now, token.ttl, this.remember);
})
.catch(res => {
if (res.data?.error?.code !== 'periodNotExceeded')
throw res;
})
.finally(() => {
this.checking = false;
});
}
stopRenewer() {
clearInterval(this.inservalId);
} }
} }
Token.$inject = ['vnInterceptor', '$http', '$rootScope'];
ngModule.service('vnToken', Token); ngModule.service('vnToken', Token);

View File

@ -42,7 +42,7 @@
<button class="buttonAccount"> <button class="buttonAccount">
<img <img
id="user" id="user"
ng-src="{{$ctrl.getImageUrl()}}" ng-src="{{::$ctrl.getImageUrl()}}"
ng-click="userPopover.show($event)" ng-click="userPopover.show($event)"
translate-attr="{title: 'Account'}" translate-attr="{title: 'Account'}"
on-error-src/> on-error-src/>
@ -93,4 +93,4 @@
</vn-list> </vn-list>
</vn-portal> </vn-portal>
<ui-view class="main-view"></ui-view> <ui-view class="main-view"></ui-view>
<vn-scroll-up></vn-scroll-up> <vn-scroll-up></vn-scroll-up>

View File

@ -3,14 +3,13 @@ import Component from 'core/lib/component';
import './style.scss'; import './style.scss';
export class Layout extends Component { export class Layout extends Component {
constructor($element, $, vnModules, vnToken) { constructor($element, $, vnModules) {
super($element, $); super($element, $);
this.modules = vnModules.get(); this.modules = vnModules.get();
} }
$onInit() { $onInit() {
this.getUserData(); this.getUserData();
this.getAccessTokenConfig();
} }
getUserData() { getUserData() {
@ -32,41 +31,11 @@ export class Layout extends Component {
window.location.reload(); window.location.reload();
} }
getAccessTokenConfig() {
this.$http.get('AccessTokenConfigs').then(json => {
const firtsResult = json.data[0];
if (!firtsResult) return;
this.renewPeriod = firtsResult.renewPeriod;
this.renewInterval = firtsResult.renewInterval;
const intervalMilliseconds = firtsResult.renewInterval * 1000;
this.inservalId = setInterval(this.checkTokenValidity.bind(this), intervalMilliseconds);
});
}
checkTokenValidity() {
const now = new Date();
const differenceMilliseconds = now - new Date(this.vnToken.created);
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
if (differenceSeconds > this.renewPeriod) {
this.$http.post('VnUsers/renewToken')
.then(json => {
if (json.data.token) {
let remember = true;
if (window.sessionStorage.vnToken) remember = false;
this.vnToken.set(json.data.token, json.data.created, remember);
}
});
}
}
$onDestroy() { $onDestroy() {
clearInterval(this.inservalId); this.vnToken.stopRenewer();
} }
} }
Layout.$inject = ['$element', '$scope', 'vnModules', 'vnToken']; Layout.$inject = ['$element', '$scope', 'vnModules'];
ngModule.vnComponent('vnLayout', { ngModule.vnComponent('vnLayout', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -37,49 +37,4 @@ describe('Component vnLayout', () => {
expect(url).not.toBeDefined(); expect(url).not.toBeDefined();
}); });
}); });
describe('getAccessTokenConfig()', () => {
it(`should set the renewPeriod and renewInterval properties in localStorage`, () => {
const response = [{
renewPeriod: 100,
renewInterval: 5
}];
$httpBackend.expect('GET', `AccessTokenConfigs`).respond(response);
controller.getAccessTokenConfig();
$httpBackend.flush();
expect(controller.renewPeriod).toBe(100);
expect(controller.renewInterval).toBe(5);
expect(controller.inservalId).toBeDefined();
});
});
describe('checkTokenValidity()', () => {
it(`should not call renewToken and not set vnToken in the controller`, () => {
controller.renewPeriod = 100;
controller.vnToken.created = new Date();
controller.checkTokenValidity();
expect(controller.vnToken.token).toBeNull();
});
it(`should call renewToken and set vnToken properties in the controller`, () => {
const response = {
token: 999,
created: new Date()
};
controller.renewPeriod = 100;
const oneHourBefore = new Date(Date.now() - (60 * 60 * 1000));
controller.vnToken.created = oneHourBefore;
$httpBackend.expect('POST', `VnUsers/renewToken`).respond(response);
controller.checkTokenValidity();
$httpBackend.flush();
expect(controller.vnToken.token).toBe(999);
expect(controller.vnToken.created).toEqual(response.created);
});
});
}); });

View File

@ -24,7 +24,8 @@
</div> </div>
<div class="user-log vn-mb-sm" ng-repeat="userLog in ::originLog.logs"> <div class="user-log vn-mb-sm" ng-repeat="userLog in ::originLog.logs">
<div class="timeline"> <div class="timeline">
<vn-avatar <div class="user-avatar">
<vn-avatar
ng-class="::{system: !userLog.user}" ng-class="::{system: !userLog.user}"
val="{{::userLog.user ? userLog.user.nickname : $ctrl.$t('System')}}" val="{{::userLog.user ? userLog.user.nickname : $ctrl.$t('System')}}"
ng-click="$ctrl.showWorkerDescriptor($event, userLog)"> ng-click="$ctrl.showWorkerDescriptor($event, userLog)">
@ -33,6 +34,7 @@
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}"> ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}">
</img> </img>
</vn-avatar> </vn-avatar>
</div>
<div class="arrow bg-panel" ng-if="::$ctrl.byRecord"></div> <div class="arrow bg-panel" ng-if="::$ctrl.byRecord"></div>
<div class="line"></div> <div class="line"></div>
</div> </div>

View File

@ -115,23 +115,21 @@ export default class Controller extends Section {
// User // User
const userChanged = originChanged const userChanged = originChanged
|| log.userFk != prevLog.userFk || log.userFk != prevLog.userFk;
|| nLogs >= 5;
if (userChanged) { if (userChanged) {
originLog.logs.push(userLog = { originLog.logs.push(userLog = {
user: log.user, user: log.user,
userFk: log.userFk, userFk: log.userFk,
logs: [] logs: []
}); });
nLogs = 0;
} }
nLogs++;
// Model // Model
const modelChanged = userChanged const modelChanged = userChanged
|| log.changedModel != prevLog.changedModel || log.changedModel != prevLog.changedModel
|| log.changedModelId != prevLog.changedModelId; || log.changedModelId != prevLog.changedModelId
|| nLogs >= 6;
if (modelChanged) { if (modelChanged) {
userLog.logs.push(modelLog = { userLog.logs.push(modelLog = {
model: log.changedModel, model: log.changedModel,
@ -140,7 +138,9 @@ export default class Controller extends Section {
showValue: log.changedModelValue, showValue: log.changedModelValue,
logs: [] logs: []
}); });
nLogs = 0;
} }
nLogs++;
modelLog.logs.push(log); modelLog.logs.push(log);

View File

@ -44,11 +44,20 @@ vn-log {
right: -4px; right: -4px;
z-index: 1; z-index: 1;
} }
& > vn-avatar { & > .user-avatar {
cursor: pointer; background-color: $color-bg;
padding: $spacing-sm 0;
margin-top: -$spacing-sm;
position: sticky;
top: 64px;
&.system { & > vn-avatar {
background-color: $color-main !important; cursor: pointer;
display: block;
&.system {
background-color: $color-main !important;
}
} }
} }
& > .line { & > .line {
@ -57,8 +66,8 @@ vn-log {
width: 2px; width: 2px;
left: 18px; left: 18px;
z-index: -1; z-index: -1;
top: 44px; top: 0;
bottom: -2px; bottom: -$spacing-sm;
} }
} }
&:last-child > .timeline > .line { &:last-child > .timeline > .line {

View File

@ -11,7 +11,8 @@ function config($stateProvider, $urlRouterProvider) {
abstract: true, abstract: true,
template: '<vn-layout></vn-layout>', template: '<vn-layout></vn-layout>',
resolve: { resolve: {
config: ['vnConfig', vnConfig => vnConfig.initialize()] config: ['vnConfig', vnConfig => vnConfig.initialize()],
token: ['vnToken', vnToken => vnToken.fetchConfig()]
} }
}) })
.state('outLayout', { .state('outLayout', {

View File

@ -175,6 +175,7 @@
"Pass expired": "The password has expired, change it from Salix", "Pass expired": "The password has expired, change it from Salix",
"Can't transfer claimed sales": "Can't transfer claimed sales", "Can't transfer claimed sales": "Can't transfer claimed sales",
"Invalid quantity": "Invalid quantity", "Invalid quantity": "Invalid quantity",
"Failed to upload delivery note": "Error to upload delivery note {{id}}", "Failed to upload delivery note": "Error to upload delivery note {{id}}",
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address" "Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address",
} "The renew period has not been exceeded": "The renew period has not been exceeded"
}

View File

@ -177,7 +177,6 @@
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria", "You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas", "Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta", "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"Sorts whole route": "Reordena ruta entera",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*", "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*", "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío", "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",

View File

@ -77,14 +77,6 @@ module.exports = Self => {
if (salesPersonId) if (salesPersonId)
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message); await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
await models.ClaimLog.create({
originFk: args.id,
userFk: userId,
action: 'insert',
description: 'Claim-pickup-order sent',
changedModel: 'Mail'
});
const email = new Email('claim-pickup-order', params); const email = new Email('claim-pickup-order', params);
return email.send(); return email.send();

View File

@ -19,9 +19,6 @@ module.exports = Self => {
}); });
Self.confirmTransaction = async(ctx, id, options) => { Self.confirmTransaction = async(ctx, id, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
let tx; let tx;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
@ -34,29 +31,8 @@ module.exports = Self => {
} }
try { try {
const oldTpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
const confirm = await Self.rawSql('CALL hedera.tpvTransaction_confirmById(?)', [id], myOptions); const confirm = await Self.rawSql('CALL hedera.tpvTransaction_confirmById(?)', [id], myOptions);
const tpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
const oldInstance = {status: oldTpvTransaction.status};
const newInstance = {status: tpvTransaction.status};
const logRecord = {
originFk: tpvTransaction.clientFk,
userFk: userId,
action: 'update',
changedModel: 'TpvTransaction',
changedModelId: id,
oldInstance: oldInstance,
newInstance: newInstance
};
await models.ClientLog.create(logRecord, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return confirm; return confirm;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -30,34 +30,9 @@ module.exports = Self => {
} }
}); });
Self.sendSms = async(ctx, id, destination, message, options) => { Self.sendSms = async(ctx, id, destination, message) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const userId = ctx.req.accessToken.userId;
const sms = await models.Sms.send(ctx, destination, message); const sms = await models.Sms.send(ctx, destination, message);
const logRecord = {
originFk: id,
userFk: userId,
action: 'insert',
changedModel: 'sms',
newInstance: {
destinationFk: id,
destination: destination,
message: message,
statusCode: sms.statusCode,
status: sms.status
}
};
const clientLog = await models.ClientLog.create(logRecord, myOptions);
sms.logId = clientLog.id;
return sms; return sms;
}; };
}; };

View File

@ -13,10 +13,7 @@ describe('client sendSms()', () => {
const sms = await models.Client.sendSms(ctx, id, destination, message, options); const sms = await models.Client.sendSms(ctx, id, destination, message, options);
const createdLog = await models.ClientLog.findById(sms.logId, null, options); expect(sms).toBeDefined();
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
expect(json.message).toEqual(message);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -144,20 +144,7 @@ module.exports = Self => {
if (taxDataChecked && !hasSageData) if (taxDataChecked && !hasSageData)
throw new UserError(`You need to fill sage information before you check verified data`); throw new UserError(`You need to fill sage information before you check verified data`);
if (args.despiteOfClient) {
const logRecord = {
originFk: clientId,
userFk: userId,
action: 'update',
changedModel: 'Client',
changedModelId: clientId,
description: $t(`Client checked as validated despite of duplication`, {
clientId: args.despiteOfClient
})
};
await models.ClientLog.create(logRecord, myOptions);
}
// Remove unwanted properties // Remove unwanted properties
delete args.ctx; delete args.ctx;
delete args.id; delete args.id;

View File

@ -489,21 +489,6 @@ module.exports = Self => {
hasChanges = oldData.name != changes.name || oldData.active != changes.active; hasChanges = oldData.name != changes.name || oldData.active != changes.active;
if (!hasChanges) return; if (!hasChanges) return;
const isClient = await Self.app.models.Client.count({id: oldData.id});
if (isClient) {
const loopBackContext = LoopBackContext.getCurrentContext();
const userId = loopBackContext.active.accessToken.userId;
const logRecord = {
originFk: oldData.id,
userFk: userId,
action: 'update',
changedModel: 'VnUser',
oldInstance: {name: oldData.name, active: oldData.active},
newInstance: {name: changes.name, active: changes.active}
};
await Self.app.models.ClientLog.create(logRecord);
}
} }
}); });
}); });

View File

@ -21,7 +21,6 @@ module.exports = Self => {
Self.guessPriority = async(ctx, id) => { Self.guessPriority = async(ctx, id) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const $t = ctx.req.__; // $translate
const query = `CALL vn.routeGuessPriority(?)`; const query = `CALL vn.routeGuessPriority(?)`;
const tx = await Self.beginTransaction({}); const tx = await Self.beginTransaction({});
@ -30,16 +29,6 @@ module.exports = Self => {
const priority = await Self.rawSql(query, [id], options); const priority = await Self.rawSql(query, [id], options);
let logRecord = {
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Route',
description: $t('Sorts whole route')
};
await Self.app.models.RouteLog.create(logRecord, options);
await tx.commit(); await tx.commit();
return priority; return priority;
} catch (e) { } catch (e) {

View File

@ -31,18 +31,6 @@ describe('route updateVolume()', () => {
const updatedRoute = await app.models.Route.findById(routeId, null, options); const updatedRoute = await app.models.Route.findById(routeId, null, options);
expect(updatedRoute.m3).not.toEqual(route.m3); expect(updatedRoute.m3).not.toEqual(route.m3);
const logs = await app.models.RouteLog.find({
fields: ['id', 'newInstance']
}, options);
const m3Log = logs.filter(log => {
if (log.newInstance)
return log.newInstance.m3 === updatedRoute.m3;
});
expect(m3Log.length).toEqual(1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();

View File

@ -35,24 +35,9 @@ module.exports = Self => {
} }
try { try {
const originalRoute = await models.Route.findById(id, null, myOptions);
await Self.rawSql(`CALL vn.routeUpdateM3(?)`, [id], myOptions); await Self.rawSql(`CALL vn.routeUpdateM3(?)`, [id], myOptions);
const updatedRoute = await models.Route.findById(id, null, myOptions); const updatedRoute = await models.Route.findById(id, null, myOptions);
await models.RouteLog.create({
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Route',
changedModelId: id,
oldInstance: {m3: originalRoute.m3},
newInstance: {m3: updatedRoute.m3}
}, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return updatedRoute; return updatedRoute;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -106,7 +106,7 @@ module.exports = Self => {
} }
} }
const query = `CALL vn.ticket_recalc(?)`; const query = `CALL vn.ticket_recalc(?, NULL)`;
await Self.rawSql(query, [refundTicket.id], myOptions); await Self.rawSql(query, [refundTicket.id], myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -96,7 +96,7 @@ module.exports = Self => {
await sale.updateAttributes({price: newPrice}, myOptions); await sale.updateAttributes({price: newPrice}, myOptions);
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions); await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [sale.ticketFk], myOptions); await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions);
const salesPerson = sale.ticket().client().salesPersonUser(); const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) { if (salesPerson) {

View File

@ -97,22 +97,6 @@ module.exports = Self => {
}); });
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions); await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
const logRecord = {
originFk: sale.ticketFk,
userFk: userId,
action: 'update',
changedModel: 'ticketRequest',
newInstance: {
destinationFk: sale.ticketFk,
quantity: sale.quantity,
concept: sale.concept,
itemId: sale.itemFk,
ticketId: sale.ticketFk,
}
};
await Self.app.models.TicketLog.create(logRecord, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return sale; return sale;

View File

@ -85,7 +85,7 @@ module.exports = Self => {
}, myOptions); }, myOptions);
await Self.rawSql('CALL vn.sale_calculateComponent(?, NULL)', [newSale.id], myOptions); await Self.rawSql('CALL vn.sale_calculateComponent(?, NULL)', [newSale.id], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions); await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
const sale = await models.Sale.findById(newSale.id, { const sale = await models.Sale.findById(newSale.id, {
include: { include: {

View File

@ -242,16 +242,6 @@ module.exports = Self => {
const oldProperties = await loggable.translateValues(Self, changes.old); const oldProperties = await loggable.translateValues(Self, changes.old);
const newProperties = await loggable.translateValues(Self, changes.new); const newProperties = await loggable.translateValues(Self, changes.new);
await models.TicketLog.create({
originFk: args.id,
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: args.id,
oldInstance: oldProperties,
newInstance: newProperties
}, myOptions);
const salesPersonId = originalTicket.client().salesPersonFk; const salesPersonId = originalTicket.client().salesPersonFk;
if (salesPersonId) { if (salesPersonId) {
const origin = ctx.req.headers.origin; const origin = ctx.req.headers.origin;

View File

@ -52,22 +52,9 @@ module.exports = Self => {
}); });
if (!ticket.originId || !ticket.destinationId) continue; if (!ticket.originId || !ticket.destinationId) continue;
const ticketDestinationLogRecord = {
originFk: ticket.destinationId,
userFk: ctx.req.accessToken.userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: ticket.destinationId,
changedModelValue: ticket.destinationId,
oldInstance: {},
newInstance: {mergedTicket: ticket.originId}
};
await models.Sale.updateAll({ticketFk: ticket.originId}, {ticketFk: ticket.destinationId}, myOptions); await models.Sale.updateAll({ticketFk: ticket.originId}, {ticketFk: ticket.destinationId}, myOptions);
if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions)) { if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions))
await models.TicketLog.create(ticketDestinationLogRecord, myOptions);
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message); await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
}
} }
if (tx) if (tx)
await tx.commit(); await tx.commit();

View File

@ -30,54 +30,12 @@ module.exports = Self => {
} }
}); });
Self.sendSms = async(ctx, id, destination, message, options) => { Self.sendSms = async(ctx, id, destination, message) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {}; const sms = await models.Sms.send(ctx, destination, message);
let tx; await models.TicketSms.create({
ticketFk: id,
if (typeof options == 'object') smsFk: sms.id
Object.assign(myOptions, options); });
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const userId = ctx.req.accessToken.userId;
try {
const sms = await models.Sms.send(ctx, destination, message);
const newTicketSms = {
ticketFk: id,
smsFk: sms.id
};
await models.TicketSms.create(newTicketSms);
const logRecord = {
originFk: id,
userFk: userId,
action: 'insert',
changedModel: 'sms',
newInstance: {
destinationFk: id,
destination: destination,
message: message,
statusCode: sms.statusCode,
status: sms.status
}
};
const ticketLog = await models.TicketLog.create(logRecord, myOptions);
sms.logId = ticketLog.id;
if (tx) await tx.commit();
return sms;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -43,12 +43,10 @@ describe('ticket merge()', () => {
await models.Ticket.merge(ctx, [tickets], options); await models.Ticket.merge(ctx, [tickets], options);
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets.destinationId}}, options);
const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options); const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options); const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
const chatNotificationAfterMerge = await models.Chat.find(null, options); const chatNotificationAfterMerge = await models.Chat.find(null, options);
expect(createdTicketLog.length).toEqual(1);
expect(deletedTicket.isDeleted).toEqual(true); expect(deletedTicket.isDeleted).toEqual(true);
expect(salesTicketFuture.length).toEqual(2); expect(salesTicketFuture.length).toEqual(2);
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2); expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);

View File

@ -12,19 +12,14 @@ describe('ticket sendSms()', () => {
const destination = 222222222; const destination = 222222222;
const message = 'this is the message created in a test'; const message = 'this is the message created in a test';
const sms = await models.Ticket.sendSms(ctx, id, destination, message, options); await models.Ticket.sendSms(ctx, id, destination, message, options);
const createdLog = await models.TicketLog.findById(sms.logId, null, options);
const filter = { const filter = {
ticketFk: createdLog.originFk ticketFk: id
}; };
const ticketSms = await models.TicketSms.findOne(filter, options); const ticketSms = await models.TicketSms.findOne(filter, options);
const json = JSON.parse(JSON.stringify(createdLog.newInstance)); expect(ticketSms.ticketFk).toEqual(id);
expect(json.message).toEqual(message);
expect(ticketSms.ticketFk).toEqual(createdLog.originFk);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -84,13 +84,6 @@ module.exports = Self => {
for (const sale of sales) { for (const sale of sales) {
const originalSale = map.get(sale.id); const originalSale = map.get(sale.id);
const originalSaleData = { // <-- Loopback modifies original instance on save
itemFk: originalSale.itemFk,
quantity: originalSale.quantity,
concept: originalSale.concept,
ticketFk: originalSale.ticketFk
};
if (sale.quantity == originalSale.quantity) { if (sale.quantity == originalSale.quantity) {
query = `UPDATE sale query = `UPDATE sale
SET ticketFk = ? SET ticketFk = ?
@ -100,48 +93,6 @@ module.exports = Self => {
await transferPartialSale( await transferPartialSale(
ticketId, originalSale, sale, myOptions); ticketId, originalSale, sale, myOptions);
} }
// Log to original ticket
await models.TicketLog.create({
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,
concept: originalSaleData.concept,
ticket: originalSaleData.ticketFk
},
newInstance: {
item: sale.itemFk,
quantity: sale.quantity,
concept: sale.concept,
ticket: ticketId
}
}, myOptions);
// Log to destination ticket
await models.TicketLog.create({
originFk: ticketId,
userFk: userId,
action: 'update',
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,
concept: originalSaleData.concept,
ticket: originalSaleData.ticketFk
},
newInstance: {
item: sale.itemFk,
quantity: sale.quantity,
concept: sale.concept,
ticket: ticketId
}
}, myOptions);
} }
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions); const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);

View File

@ -147,7 +147,7 @@ module.exports = Self => {
await Promise.all(promises); await Promise.all(promises);
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions); await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions); await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
const ticket = await models.Ticket.findById(id, { const ticket = await models.Ticket.findById(id, {
include: { include: {

View File

@ -1,30 +1,4 @@
const LoopBackContext = require('loopback-context');
module.exports = Self => { module.exports = Self => {
// Methods // Methods
require('./ticket-methods')(Self); require('./ticket-methods')(Self);
Self.observe('before save', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = loopBackContext.active;
if (ctx.isNewInstance) return;
let changes = ctx.data || ctx.instance;
if (changes.routeFk === null && ctx.currentInstance.routeFk != null) {
let instance = JSON.parse(JSON.stringify(ctx.currentInstance));
let userId = httpCtx.accessToken.userId;
let logRecord = {
originFk: ctx.currentInstance.routeFk,
userFk: userId,
action: 'delete',
changedModel: 'Route',
oldInstance: {ticket: instance.id},
newInstance: null
};
await Self.app.models.RouteLog.create(logRecord);
}
});
}; };

View File

@ -156,7 +156,7 @@
icon="install_mobile" icon="install_mobile"
ng-show="$ctrl.totalChecked > 0" ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.sendDocuware()" ng-click="$ctrl.sendDocuware()"
vn-tooltip="Set as delivered and open delivery note(s)" vn-tooltip="Set as delivered and send delivery note(s) to the tablet"
tooltip-position="left"> tooltip-position="left">
</vn-button> </vn-button>
<vn-button class="round vn-mb-sm" <vn-button class="round vn-mb-sm"

View File

@ -3,7 +3,7 @@ Go to lines: Ir a lineas
Not available: No disponible Not available: No disponible
Not visible: No visible Not visible: No visible
Payment on account...: Pago a cuenta... Payment on account...: Pago a cuenta...
Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es Set as delivered and send delivery note(s) to the tablet: Marcar como servido/s y enviar albarán/es a la tablet
Closure: Cierre Closure: Cierre
You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes
Filter by selection: Filtro por selección Filter by selection: Filtro por selección
@ -17,4 +17,4 @@ Quick invoice: Factura rápida
Multiple invoice: Factura múltiple Multiple invoice: Factura múltiple
Make invoice...: Crear factura... Make invoice...: Crear factura...
Invoice selected tickets: Facturar tickets seleccionados Invoice selected tickets: Facturar tickets seleccionados
Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets? Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets?

View File

@ -25,9 +25,7 @@ module.exports = Self => {
}); });
Self.cloneWithEntries = async(ctx, id) => { Self.cloneWithEntries = async(ctx, id) => {
const userId = ctx.req.accessToken.userId;
const conn = Self.dataSource.connector; const conn = Self.dataSource.connector;
const models = Self.app.models;
const travel = await Self.findById(id, { const travel = await Self.findById(id, {
fields: [ fields: [
'id', 'id',
@ -81,18 +79,6 @@ module.exports = Self => {
] ]
}); });
const oldProperties = await loggable.translateValues(Self, travel);
const newProperties = await loggable.translateValues(Self, newTravel);
await models.TravelLog.create({
originFk: newTravel.id,
userFk: userId,
action: 'insert',
changedModel: 'Travel',
changedModelId: newTravel.id,
oldInstance: oldProperties,
newInstance: newProperties
});
return newTravel.id; return newTravel.id;
}; };
}; };

View File

@ -30,24 +30,8 @@ module.exports = Self => {
SET travelFk = NULL, dmsFk = NULL SET travelFk = NULL, dmsFk = NULL
WHERE id = ?`, [id], {userId}); WHERE id = ?`, [id], {userId});
const oldInstance = {
travelFk: travelThermograph.travelFk,
dmsFk: travelThermograph.dmsFk
};
await models.TravelLog.create({
originFk: travelThermograph.travelFk,
userFk: userId,
action: 'delete',
changedModel: 'TravelThermograph',
changedModelId: id,
oldInstance: oldInstance,
newInstance: {}
});
travelThermograph.travelFk = null; travelThermograph.travelFk = null;
travelThermograph.dmsFk = null; travelThermograph.dmsFk = null;
return travelThermograph; return travelThermograph;
}; };
}; };

View File

@ -37,6 +37,16 @@ class Controller extends SearchPanel {
} }
applyFilters(param) { applyFilters(param) {
if (typeof this.filter.scopeDays === 'number') {
const shippedFrom = Date.vnNew();
shippedFrom.setHours(0, 0, 0, 0);
const shippedTo = new Date(shippedFrom.getTime());
shippedTo.setDate(shippedTo.getDate() + this.filter.scopeDays);
shippedTo.setHours(23, 59, 59, 999);
Object.assign(this.filter, {shippedFrom, shippedTo});
}
this.model.applyFilter({}, this.filter) this.model.applyFilter({}, this.filter)
.then(() => { .then(() => {
if (param && this.model._orgData.length === 1) if (param && this.model._orgData.length === 1)

View File

@ -92,7 +92,7 @@
</span> </span>
</vn-td> </vn-td>
<vn-td expand>{{entry.supplierName}}</vn-td> <vn-td expand>{{entry.supplierName}}</vn-td>
<vn-td shrink>{{entry.ref}}</vn-td> <vn-td shrink>{{entry.reference}}</vn-td>
<vn-td shrink>{{entry.hb}}</vn-td> <vn-td shrink>{{entry.hb}}</vn-td>
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td> <vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
<vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td> <vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td>

View File

@ -40,7 +40,6 @@ module.exports = Self => {
Self.updateWorkerTimeControlMail = async(ctx, options) => { Self.updateWorkerTimeControlMail = async(ctx, options) => {
const models = Self.app.models; const models = Self.app.models;
const args = ctx.args; const args = ctx.args;
const userId = ctx.req.accessToken.userId;
const myOptions = {}; const myOptions = {};
@ -57,9 +56,6 @@ module.exports = Self => {
if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`); if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`);
const oldState = workerTimeControlMail.state;
const oldReason = workerTimeControlMail.reason;
await workerTimeControlMail.updateAttributes({ await workerTimeControlMail.updateAttributes({
state: args.state, state: args.state,
reason: args.reason || null reason: args.reason || null
@ -70,22 +66,5 @@ module.exports = Self => {
sendedCounter: workerTimeControlMail.sendedCounter + 1 sendedCounter: workerTimeControlMail.sendedCounter + 1
}, myOptions); }, myOptions);
} }
const logRecord = {
originFk: args.workerId,
userFk: userId,
action: 'update',
changedModel: 'WorkerTimeControlMail',
oldInstance: {
state: oldState,
reason: oldReason
},
newInstance: {
state: args.state,
reason: args.reason
}
};
return models.WorkerLog.create(logRecord, myOptions);
}; };
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-back", "name": "salix-back",
"version": "23.28.01", "version": "23.26.01",
"author": "Verdnatura Levante SL", "author": "Verdnatura Levante SL",
"description": "Salix backend", "description": "Salix backend",
"license": "GPL-3.0", "license": "GPL-3.0",