Merge branch 'dev' into 5284-fueraPlazo
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
8499ae5243
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -5,16 +5,28 @@ 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).
|
||||||
|
|
||||||
## [2308.01] - 2023-03-09
|
## [2310.01] - 2023-03-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Client -> Descriptor) Nuevo icono $ con barrotes para los clientes con impago
|
-
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
-
|
-
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
-
|
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
|
||||||
|
- (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz
|
||||||
|
|
||||||
|
## [2308.01] - 2023-03-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Proveedores -> Datos fiscales) Añadido checkbox 'Vies'
|
||||||
|
- (Client -> Descriptor) Nuevo icono $ con barrotes para los clientes con impago
|
||||||
|
- (Trabajador -> Datos Básicos) Añadido nuevo campo Taquilla
|
||||||
|
- (Trabajador -> PDA) Nueva sección
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- (Ticket -> Borrar ticket) Restringido el borrado de tickets con abono
|
||||||
|
|
||||||
## [2306.01] - 2023-02-23
|
## [2306.01] - 2023-02-23
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,10 @@ async function test() {
|
||||||
|
|
||||||
const bootOptions = {dataSources};
|
const bootOptions = {dataSources};
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
app.boot(bootOptions);
|
await new Promise((resolve, reject) => {
|
||||||
|
app.boot(bootOptions,
|
||||||
|
err => err ? reject(err) : resolve());
|
||||||
|
});
|
||||||
|
|
||||||
const Jasmine = require('jasmine');
|
const Jasmine = require('jasmine');
|
||||||
const jasmine = new Jasmine();
|
const jasmine = new Jasmine();
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES('ClaimBeginning', 'isEditable', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||||
|
|
||||||
|
DELETE FROM `salix`.`ACL`
|
||||||
|
WHERE model='Claim' AND property='isEditable';
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
ALTER TABLE `vn`.`supplier` ADD `isVies` tinyint(4) DEFAULT 0 NOT NULL;
|
||||||
|
|
||||||
|
UPDATE `vn`.`supplier` s
|
||||||
|
JOIN vn.country c ON c.id = s.countryFk
|
||||||
|
SET s.nif = MID(s.nif, 3, LENGTH(s.nif)-1), s.isVies = TRUE
|
||||||
|
WHERE s.nif <> TRIM(IF(c.code = LEFT(s.nif, 2), MID(s.nif, 3, LENGTH(s.nif)-1), s.nif));
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `vn`.`chat`
|
||||||
|
(senderFk, recipient, checkUserStatus, message, status, attempts)
|
||||||
|
VALUES(19263, '#informatica-cau', 0, '
|
||||||
|
```
|
||||||
|
UPDATE `vn`.`supplier` s
|
||||||
|
JOIN vn.country c ON c.id = s.countryFk
|
||||||
|
SET s.nif = MID(s.nif, 3, LENGTH(s.nif)-1), s.isVies = TRUE
|
||||||
|
WHERE s.nif <> TRIM(IF(c.code = LEFT(s.nif, 2), MID(s.nif, 3, LENGTH(s.nif)-1), s.nif));
|
||||||
|
```', 0, 0);
|
|
@ -0,0 +1,15 @@
|
||||||
|
ALTER TABLE `vn`.`worker` ADD locker INT UNSIGNED NULL UNIQUE;
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('DeviceProduction', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionModels', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionState', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionUser', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProduction', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionModels', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionState', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionUser', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('Worker', 'deallocatePDA', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('Worker', 'allocatePDA', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('Worker', 'deallocatePDA', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('Worker', 'allocatePDA', '*', 'ALLOW', 'ROLE', 'productionAssi');
|
|
@ -0,0 +1,127 @@
|
||||||
|
DROP PROCEDURE IF EXISTS `sage`.`clientSupplier_add`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE DEFINER=`root`@`localhost` PROCEDURE `sage`.`clientSupplier_add`(vCompanyFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Prepara los datos de clientes y proveedores para exportarlos a Sage
|
||||||
|
* @vCompanyFk Empresa dela que se quiere trasladar datos
|
||||||
|
*/
|
||||||
|
DECLARE vCountryCeutaMelillaFk INT;
|
||||||
|
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2);
|
||||||
|
|
||||||
|
SELECT SiglaNacion INTO vCountryCanariasCode
|
||||||
|
FROM Naciones
|
||||||
|
WHERE Nacion ='ISLAS CANARIAS';
|
||||||
|
|
||||||
|
SELECT CodigoNacion, SiglaNacion INTO vCountryCeutaMelillaFk, vCountryCeutaMelillaCode
|
||||||
|
FROM Naciones
|
||||||
|
WHERE Nacion ='CEUTA Y MELILLA';
|
||||||
|
|
||||||
|
TRUNCATE TABLE clientesProveedores;
|
||||||
|
|
||||||
|
INSERT INTO clientesProveedores
|
||||||
|
(CodigoEmpresa,
|
||||||
|
ClienteOProveedor,
|
||||||
|
CodigoClienteProveedor,
|
||||||
|
RazonSocial,
|
||||||
|
Nombre,
|
||||||
|
Domicilio,
|
||||||
|
CodigoCuenta,
|
||||||
|
CifDni,
|
||||||
|
CifEuropeo,
|
||||||
|
CodigoPostal,
|
||||||
|
Municipio,
|
||||||
|
CodigoProvincia,
|
||||||
|
Provincia,
|
||||||
|
CodigoNacion,
|
||||||
|
SiglaNacion,
|
||||||
|
PersonaFisicaJuridica,
|
||||||
|
TipoDocumentoPersona,
|
||||||
|
CodigoIva,
|
||||||
|
Nacion,
|
||||||
|
Telefono,
|
||||||
|
Telefono2,
|
||||||
|
CodigoTransaccion,
|
||||||
|
CodigoRetencion,
|
||||||
|
Email1,
|
||||||
|
iban)
|
||||||
|
SELECT
|
||||||
|
company_getCode(vCompanyFk),
|
||||||
|
'C',
|
||||||
|
c.id,
|
||||||
|
c.socialName,
|
||||||
|
c.socialName,
|
||||||
|
IFNULL(c.street, ''),
|
||||||
|
c.accountingAccount,
|
||||||
|
TRIM(IF(c.isVies, CONCAT(cu.code,c.fi), c.fi)),
|
||||||
|
IF(n.NacionCEE,TRIM(IF(cu.code = LEFT(c.fi, 2), c.fi, CONCAT(cu.code,c.fi))) , ''),
|
||||||
|
IFNULL(c.postcode, ''),
|
||||||
|
IFNULL(c.city, ''),
|
||||||
|
IFNULL(pr.CodigoProvincia, ''),
|
||||||
|
IFNULL(p.name, ''),
|
||||||
|
IF(n.SiglaNacion = vCountryCanariasCode COLLATE utf8mb3_unicode_ci, IF(@isCeutaMelilla := IF(pr.Provincia IN ('CEUTA', 'MELILLA'), TRUE, FALSE), vCountryCeutaMelillaFk, IF (@isCanarias, vCountryCanariasCode, n.CodigoNacion)), n.CodigoNacion),
|
||||||
|
IF(n.SiglaNacion = vCountryCanariasCode COLLATE utf8mb3_unicode_ci, IF(@isCeutaMelilla, vCountryCeutaMelillaCode, IF (@isCanarias, vCountryCanariasCode, n.SiglaNacion)), n.SiglaNacion),
|
||||||
|
IF((c.fi REGEXP '^([[:blank:]]|[[:digit:]])'), 'J','F'),
|
||||||
|
IF(cu.code IN('ES','EX'),
|
||||||
|
1,
|
||||||
|
IF((cu.isUeeMember AND c.isVies), 2, 4)),
|
||||||
|
IFNULL(c.taxTypeSageFk,0),
|
||||||
|
IF(n.SiglaNacion = vCountryCanariasCode COLLATE utf8mb3_unicode_ci,
|
||||||
|
IF(@isCeutaMelilla, 'CEUTA Y MELILLA', IF (@isCanarias, 'ISLAS CANARIAS', n.Nacion)),
|
||||||
|
n.Nacion),
|
||||||
|
IFNULL(c.phone, ''),
|
||||||
|
IFNULL(c.mobile, ''),
|
||||||
|
IFNULL(c.transactionTypeSageFk, 0),
|
||||||
|
'0',
|
||||||
|
IFNULL(SUBSTR(c.email, 1, LOCATE(',', CONCAT(c.email, ','))-1), ''),
|
||||||
|
IFNULL(c.iban, '')
|
||||||
|
FROM vn.`client` c
|
||||||
|
JOIN clientLastTwoMonths clm ON clm.clientFk = c.id
|
||||||
|
LEFT JOIN vn.country cu ON cu.id = c.countryFk
|
||||||
|
LEFT JOIN Naciones n ON n.countryFk = cu.id
|
||||||
|
LEFT JOIN vn.province p ON p.id = c.provinceFk
|
||||||
|
LEFT JOIN Provincias pr ON pr.provinceFk = p.id
|
||||||
|
WHERE c.isRelevant
|
||||||
|
AND clm.companyFk = vCompanyFk
|
||||||
|
UNION ALL
|
||||||
|
SELECT company_getCode(vCompanyFk),
|
||||||
|
'P',
|
||||||
|
s.id,
|
||||||
|
s.name,
|
||||||
|
s.name,
|
||||||
|
IFNULL(s.street, ''),
|
||||||
|
s.account,
|
||||||
|
TRIM(IF(s.isVies, CONCAT(co.code,s.nif), s.nif)),
|
||||||
|
IF(n.NacionCEE, TRIM(CONCAT(co.code, IF(co.code = LEFT(s.nif, 2), MID(s.nif, 3, LENGTH(s.nif) - 1), s.nif))), ''),
|
||||||
|
IFNULL(s.postCode,''),
|
||||||
|
IFNULL(s.city, ''),
|
||||||
|
IFNULL(pr.CodigoProvincia, ''),
|
||||||
|
IFNULL(p.name, ''),
|
||||||
|
n.CodigoNacion,
|
||||||
|
n.SiglaNacion COLLATE utf8mb3_unicode_ci,
|
||||||
|
IF((s.nif REGEXP '^([[:blank:]]|[[:digit:]])'),'J','F'),
|
||||||
|
IF(co.country IN ('España', 'España exento'), 1,IF(co.isUeeMember = 1, 2, 4)),
|
||||||
|
IFNULL(s.taxTypeSageFk, 0),
|
||||||
|
n.Nacion,
|
||||||
|
IFNULL(sc.phone, ''),
|
||||||
|
IFNULL(sc.mobile, ''),
|
||||||
|
IFNULL(s.transactionTypeSageFk, 0),
|
||||||
|
IFNULL(s.withholdingSageFk, '0'),
|
||||||
|
IFNULL(SUBSTR(sc.email, 1, (COALESCE(NULLIF(LOCATE(',', sc.email), 0), 99) - 1)), ''),
|
||||||
|
IFNULL(iban, '')
|
||||||
|
FROM vn.supplier s
|
||||||
|
JOIN supplierLastThreeMonths pl ON pl.supplierFk = s.id
|
||||||
|
LEFT JOIN vn.country co ON co.id = s.countryFk
|
||||||
|
LEFT JOIN Naciones n ON n.countryFk = co.id
|
||||||
|
LEFT JOIN vn.province p ON p.id = s.provinceFk
|
||||||
|
LEFT JOIN Provincias pr ON pr.provinceFk = p.id
|
||||||
|
LEFT JOIN vn.supplierContact sc ON sc.supplierFk = s.id
|
||||||
|
LEFT JOIN vn.supplierAccount sa ON sa.supplierFk = s.id
|
||||||
|
WHERE pl.companyFk = vCompanyFk AND
|
||||||
|
s.isActive AND
|
||||||
|
s.nif <> ''
|
||||||
|
GROUP BY pl.supplierFk, pl.companyFk;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -1759,12 +1759,12 @@ INSERT INTO `vn`.`clientSample`(`id`, `clientFk`, `typeFk`, `created`, `workerFk
|
||||||
INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`, `hasToNotify`)
|
INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`, `hasToNotify`)
|
||||||
VALUES
|
VALUES
|
||||||
( 1, 'pending', 'Pendiente', 1, 1, 0),
|
( 1, 'pending', 'Pendiente', 1, 1, 0),
|
||||||
( 2, 'managed', 'Gestionado', 1, 5, 0),
|
( 2, 'managed', 'Gestionado', 72, 5, 0),
|
||||||
( 3, 'resolved', 'Resuelto', 72, 7, 0),
|
( 3, 'resolved', 'Resuelto', 72, 7, 0),
|
||||||
( 4, 'canceled', 'Anulado', 72, 6, 1),
|
( 4, 'canceled', 'Anulado', 72, 6, 1),
|
||||||
( 5, 'incomplete', 'Incompleta', 72, 3, 1),
|
( 5, 'incomplete', 'Incompleta', 1, 3, 1),
|
||||||
( 6, 'mana', 'Mana', 1, 4, 0),
|
( 6, 'mana', 'Mana', 72, 4, 0),
|
||||||
( 7, 'lack', 'Faltas', 1, 2, 0);
|
( 7, 'lack', 'Faltas', 72, 2, 0);
|
||||||
|
|
||||||
INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`, `rma`)
|
INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`, `rma`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -1828,7 +1828,12 @@ INSERT INTO vn.claimRma (`id`, `code`, `created`, `workerFk`)
|
||||||
(4, '02676A049183', DEFAULT, 1107),
|
(4, '02676A049183', DEFAULT, 1107),
|
||||||
(5, '01837B023653', DEFAULT, 1106);
|
(5, '01837B023653', DEFAULT, 1106);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`claimLog` (`originFk`, userFk, `action`, changedModel, oldInstance, newInstance, changedModelId, `description`)
|
||||||
|
VALUES
|
||||||
|
(1, 18, 'update', 'Claim', '{"hasToPickUp":false}', '{"hasToPickUp":true}', 1, NULL),
|
||||||
|
(1, 18, 'update', 'ClaimObservation', '{}', '{"claimFk":1,"text":"Waiting for customer"}', 1, NULL),
|
||||||
|
(1, 18, 'insert', 'ClaimBeginning', '{}', '{"claimFk":1,"saleFk":1,"quantity":10}', 1, NULL),
|
||||||
|
(1, 18, 'insert', 'ClaimDms', '{}', '{"claimFk":1,"dmsFk":1}', 1, NULL);
|
||||||
|
|
||||||
INSERT INTO `hedera`.`tpvMerchant`(`id`, `description`, `companyFk`, `bankFk`, `secretKey`)
|
INSERT INTO `hedera`.`tpvMerchant`(`id`, `description`, `companyFk`, `bankFk`, `secretKey`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2760,7 +2765,6 @@ INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldIns
|
||||||
(7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL),
|
(7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL),
|
||||||
(7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'");
|
(7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'");
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
|
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
|
||||||
VALUES
|
VALUES
|
||||||
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
|
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
|
||||||
|
@ -2787,3 +2791,34 @@ INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, NULL, 1);
|
(1, NULL, 1);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`)
|
||||||
|
VALUES
|
||||||
|
(1, 12);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionModels` (`code`)
|
||||||
|
VALUES
|
||||||
|
('BLACKVIEW'),
|
||||||
|
('DODGEE'),
|
||||||
|
('ZEBRA');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionState` (`code`, `description`)
|
||||||
|
VALUES
|
||||||
|
('active', 'activo'),
|
||||||
|
('idle', 'inactivo'),
|
||||||
|
('lost', 'perdida'),
|
||||||
|
('repair', 'reparación'),
|
||||||
|
('retired', 'retirada');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProduction` (`imei`, `modelFk`, `macWifi`, `serialNumber`, `android_id`, `purchased`, `stateFk`, `isInScalefusion`, `description`)
|
||||||
|
VALUES
|
||||||
|
('ime1', 'BLACKVIEW', 'macWifi1', 'serialNumber1', 'android_id1', util.VN_NOW(), 'active', 0, NULL),
|
||||||
|
('ime2', 'DODGEE', 'macWifi2', 'serialNumber2', 'android_id2', util.VN_NOW(), 'idle', 0, NULL),
|
||||||
|
('ime3', 'ZEBRA', 'macWifi3', 'serialNumber3', 'android_id3', util.VN_NOW(), 'active', 0, NULL),
|
||||||
|
('ime4', 'BLACKVIEW', 'macWifi4', 'serialNumber4', 'android_id4', util.VN_NOW(), 'idle', 0, NULL);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionUser` (`deviceProductionFk`, `userFk`, `created`)
|
||||||
|
VALUES
|
||||||
|
(1, 1, util.VN_NOW()),
|
||||||
|
(3, 3, util.VN_NOW());
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('ticket ticketCalculateClon()', () => {
|
||||||
originalTicketId: 11
|
originalTicketId: 11
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
|
stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -31,7 +31,8 @@ describe('ticket ticketCalculateClon()', () => {
|
||||||
params.agencyType,
|
params.agencyType,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ describe('ticket ticketCalculateClon()', () => {
|
||||||
originalTicketId: 11
|
originalTicketId: 11
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
|
stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -80,7 +81,8 @@ describe('ticket ticketCalculateClon()', () => {
|
||||||
params.agencyType,
|
params.agencyType,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
describe('ticket ticketCreateWithUser()', () => {
|
describe('ticket ticket_add()', () => {
|
||||||
const today = Date.vnNew();
|
const today = Date.vnNew();
|
||||||
it('should confirm the procedure creates the expected ticket', async() => {
|
it('should confirm the procedure creates the expected ticket', async() => {
|
||||||
let stmts = [];
|
let stmts = [];
|
||||||
|
@ -21,7 +21,7 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
userId: 18
|
userId: 18
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -30,7 +30,8 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
params.agencyModeFk,
|
params.agencyModeFk,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
userId: 18
|
userId: 18
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)', [
|
stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)', [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -79,7 +80,8 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
params.agencyModeFk,
|
params.agencyModeFk,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
@ -120,7 +122,7 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
userId: 18
|
userId: 18
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -129,7 +131,8 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
params.agencyModeFk,
|
params.agencyModeFk,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
@ -172,7 +175,7 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
|
||||||
params.clientFk,
|
params.clientFk,
|
||||||
params.shipped,
|
params.shipped,
|
||||||
params.warehouseFk,
|
params.warehouseFk,
|
||||||
|
@ -181,7 +184,8 @@ describe('ticket ticketCreateWithUser()', () => {
|
||||||
params.agencyModeFk,
|
params.agencyModeFk,
|
||||||
params.routeFk,
|
params.routeFk,
|
||||||
params.landed,
|
params.landed,
|
||||||
params.userId
|
params.userId,
|
||||||
|
true
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
stmts.push(`select @newTicketId`);
|
stmts.push(`select @newTicketId`);
|
||||||
|
|
|
@ -22,6 +22,7 @@ export default {
|
||||||
userConfigSecondAutocomplete: '#localBank',
|
userConfigSecondAutocomplete: '#localBank',
|
||||||
userConfigThirdAutocomplete: '#localCompany',
|
userConfigThirdAutocomplete: '#localCompany',
|
||||||
acceptButton: '.vn-confirm.shown button[response=accept]',
|
acceptButton: '.vn-confirm.shown button[response=accept]',
|
||||||
|
cancelButton: '.vn-confirm.shown input[response=cancel]',
|
||||||
searchButton: 'vn-searchbar vn-icon[icon="search"]'
|
searchButton: 'vn-searchbar vn-icon[icon="search"]'
|
||||||
},
|
},
|
||||||
moduleIndex: {
|
moduleIndex: {
|
||||||
|
@ -973,6 +974,7 @@ export default {
|
||||||
id: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(3) > section > span',
|
id: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(3) > section > span',
|
||||||
email: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(4) > section > span',
|
email: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(4) > section > span',
|
||||||
department: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(5) > section > span',
|
department: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(5) > section > span',
|
||||||
|
locker: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(10) > section > span',
|
||||||
userId: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(2) > section > span',
|
userId: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(2) > section > span',
|
||||||
userName: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(3) > section > span',
|
userName: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(3) > section > span',
|
||||||
role: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(4) > section > span',
|
role: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(4) > section > span',
|
||||||
|
@ -983,6 +985,7 @@ export default {
|
||||||
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
|
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
|
||||||
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
|
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
|
||||||
phone: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.phone"]',
|
phone: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.phone"]',
|
||||||
|
locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]',
|
||||||
saveButton: 'vn-worker-basic-data button[type=submit]'
|
saveButton: 'vn-worker-basic-data button[type=submit]'
|
||||||
},
|
},
|
||||||
workerPbx: {
|
workerPbx: {
|
||||||
|
@ -1040,6 +1043,12 @@ export default {
|
||||||
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
|
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
|
||||||
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
||||||
},
|
},
|
||||||
|
workerPda: {
|
||||||
|
currentPDA: 'vn-worker-pda vn-textfield[ng-model="$ctrl.currentPDA.description"]',
|
||||||
|
newPDA: 'vn-worker-pda vn-autocomplete[ng-model="$ctrl.newPDA"]',
|
||||||
|
delete: 'vn-worker-pda vn-icon-button[icon=delete]',
|
||||||
|
submit: 'vn-worker-pda vn-submit[label="Assign"]',
|
||||||
|
},
|
||||||
invoiceOutIndex: {
|
invoiceOutIndex: {
|
||||||
topbarSearch: 'vn-searchbar',
|
topbarSearch: 'vn-searchbar',
|
||||||
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
|
|
|
@ -29,5 +29,6 @@ describe('Worker summary path', () => {
|
||||||
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
|
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
|
||||||
expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
|
expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
|
||||||
expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
|
expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
|
||||||
|
expect(await page.getProperty(selectors.workerSummary.locker, 'innerText')).toEqual('-');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe('Worker basic data path', () => {
|
||||||
await page.overwrite(selectors.workerBasicData.name, 'David C.');
|
await page.overwrite(selectors.workerBasicData.name, 'David C.');
|
||||||
await page.overwrite(selectors.workerBasicData.surname, 'H.');
|
await page.overwrite(selectors.workerBasicData.surname, 'H.');
|
||||||
await page.overwrite(selectors.workerBasicData.phone, '444332211');
|
await page.overwrite(selectors.workerBasicData.phone, '444332211');
|
||||||
|
await page.overwrite(selectors.workerBasicData.locker, '1');
|
||||||
await page.click(selectors.workerBasicData.saveButton);
|
await page.click(selectors.workerBasicData.saveButton);
|
||||||
|
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
@ -36,5 +37,6 @@ describe('Worker basic data path', () => {
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerBasicData.locker, 'value')).toEqual('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Worker pda path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('hr', 'worker');
|
||||||
|
await page.accessToSearchResult('employeeNick');
|
||||||
|
await page.accessToSection('worker.card.pda');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if worker has already a PDA allocated', async() => {
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerPda.currentPDA, 'value')).toContain('serialNumber1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deallocate the PDA', async() => {
|
||||||
|
await page.waitToClick(selectors.workerPda.delete);
|
||||||
|
let message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('PDA deallocated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allocate a new PDA', async() => {
|
||||||
|
await page.autocompleteSearch(selectors.workerPda.newPDA, 'serialNumber2');
|
||||||
|
await page.waitToClick(selectors.workerPda.submit);
|
||||||
|
let message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('PDA allocated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if a new PDA has been allocated', async() => {
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerPda.currentPDA, 'value')).toContain('serialNumber2');
|
||||||
|
});
|
||||||
|
});
|
|
@ -227,9 +227,21 @@ describe('Ticket Edit sale path', () => {
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
|
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
|
||||||
|
await page.waitForSnackbar();
|
||||||
await page.waitForState('ticket.card.sale');
|
await page.waitForState('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show error trying to delete a ticket with a refund', async() => {
|
||||||
|
await page.accessToSearchResult('16');
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
|
||||||
|
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Tickets with associated refunds can\'t be deleted');
|
||||||
|
await page.waitToClick(selectors.globalItems.cancelButton);
|
||||||
|
});
|
||||||
|
|
||||||
it('should select the third sale and create a claim of it', async() => {
|
it('should select the third sale and create a claim of it', async() => {
|
||||||
await page.accessToSearchResult('16');
|
await page.accessToSearchResult('16');
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
|
|
|
@ -4,12 +4,17 @@ import getBrowser from '../../helpers/puppeteer';
|
||||||
describe('Ticket Future path', () => {
|
describe('Ticket Future path', () => {
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
|
let httpRequest;
|
||||||
|
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('employee', 'ticket');
|
await page.loginAndModule('employee', 'ticket');
|
||||||
await page.accessToSection('ticket.future');
|
await page.accessToSection('ticket.future');
|
||||||
|
page.on('request', req => {
|
||||||
|
if (req.url().includes(`Tickets/getTicketsFuture`))
|
||||||
|
httpRequest = req.url();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async() => {
|
afterAll(async() => {
|
||||||
|
@ -42,114 +47,82 @@ describe('Ticket Future path', () => {
|
||||||
it('should search with the required data', async() => {
|
it('should search with the required data', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
|
expect(httpRequest).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search with the origin IPT', async() => {
|
it('should search with the origin IPT', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
await page.clearInput(selectors.ticketFuture.ipt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureIpt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.state);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
|
||||||
|
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal');
|
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal');
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
|
expect(httpRequest).toContain('ipt=H');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search with the destination IPT', async() => {
|
it('should search with the destination IPT', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
await page.clearInput(selectors.ticketFuture.ipt);
|
await page.clearInput(selectors.ticketFuture.ipt);
|
||||||
await page.clearInput(selectors.ticketFuture.futureIpt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.state);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
|
||||||
|
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal');
|
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal');
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
|
expect(httpRequest).toContain('futureIpt=H');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search with the origin grouped state', async() => {
|
it('should search with the origin grouped state', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
await page.clearInput(selectors.ticketFuture.ipt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureIpt);
|
await page.clearInput(selectors.ticketFuture.futureIpt);
|
||||||
await page.clearInput(selectors.ticketFuture.state);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
|
||||||
|
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
|
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 3);
|
|
||||||
|
expect(httpRequest).toContain('state=FREE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search with the destination grouped state', async() => {
|
it('should search with the destination grouped state', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
await page.clearInput(selectors.ticketFuture.ipt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureIpt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.state);
|
await page.clearInput(selectors.ticketFuture.state);
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
|
||||||
|
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
|
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
|
|
||||||
|
expect(httpRequest).toContain('futureState=FREE');
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
await page.clearInput(selectors.ticketFuture.ipt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureIpt);
|
|
||||||
await page.clearInput(selectors.ticketFuture.state);
|
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
await page.clearInput(selectors.ticketFuture.futureState);
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search in smart-table with an ID Origin', async() => {
|
it('should search in smart-table with an ID Origin', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
await page.write(selectors.ticketFuture.tableId, '13');
|
await page.write(selectors.ticketFuture.tableId, '1');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 2);
|
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('id');
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should search in smart-table with an ID Destination', async() => {
|
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
|
||||||
await page.write(selectors.ticketFuture.tableFutureId, '12');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 5);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should search in smart-table with an IPT Origin', async() => {
|
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.tableIpt, 'Vertical');
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search in smart-table with an IPT Destination', async() => {
|
it('should search in smart-table with an IPT Destination', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Vertical');
|
await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Horizontal');
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
|
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('futureIpt');
|
||||||
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should search in smart-table with an ID Destination', async() => {
|
||||||
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
|
await page.write(selectors.ticketFuture.tableFutureId, '1');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('futureId');
|
||||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the three last tickets and move to the future', async() => {
|
it('should check the three last tickets and move to the future', async() => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
|
||||||
describe('Ticket Advance path', () => {
|
describe('Ticket Advance path', () => {
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
const httpRequests = [];
|
let httpRequest;
|
||||||
|
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
|
@ -13,7 +13,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.accessToSection('ticket.advance');
|
await page.accessToSection('ticket.advance');
|
||||||
page.on('request', req => {
|
page.on('request', req => {
|
||||||
if (req.url().includes(`Tickets/getTicketsAdvance`))
|
if (req.url().includes(`Tickets/getTicketsAdvance`))
|
||||||
httpRequests.push(req.url());
|
httpRequest = req.url();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
await page.waitToClick(selectors.ticketAdvance.submit);
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
expect(httpRequests.length).toBeGreaterThan(0);
|
expect(httpRequest).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should search with the origin IPT', async() => {
|
it('should search with the origin IPT', async() => {
|
||||||
|
@ -57,11 +57,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal');
|
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal');
|
||||||
await page.waitToClick(selectors.ticketAdvance.submit);
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
const request = httpRequests.find(req => req.includes(('futureIpt=H')));
|
expect(httpRequest).toContain('futureIpt=H');
|
||||||
|
|
||||||
expect(request).toBeDefined();
|
|
||||||
|
|
||||||
httpRequests.splice(httpRequests.indexOf(request), 1);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
await page.clearInput(selectors.ticketAdvance.futureIpt);
|
await page.clearInput(selectors.ticketAdvance.futureIpt);
|
||||||
|
@ -73,11 +69,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
|
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
|
||||||
await page.waitToClick(selectors.ticketAdvance.submit);
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
const request = httpRequests.find(req => req.includes(('ipt=H')));
|
expect(httpRequest).toContain('ipt=H');
|
||||||
|
|
||||||
expect(request).toBeDefined();
|
|
||||||
|
|
||||||
httpRequests.splice(httpRequests.indexOf(request), 1);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
await page.clearInput(selectors.ticketAdvance.ipt);
|
await page.clearInput(selectors.ticketAdvance.ipt);
|
||||||
|
@ -88,11 +80,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||||
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical');
|
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical');
|
||||||
|
|
||||||
const request = httpRequests.find(req => req.includes(('futureIpt')));
|
expect(httpRequest).toContain('futureIpt');
|
||||||
|
|
||||||
expect(request).toBeDefined();
|
|
||||||
|
|
||||||
httpRequests.splice(httpRequests.indexOf(request), 1);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
@ -103,11 +91,7 @@ describe('Ticket Advance path', () => {
|
||||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||||
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical');
|
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical');
|
||||||
|
|
||||||
const request = httpRequests.find(req => req.includes(('ipt')));
|
expect(httpRequest).toContain('ipt');
|
||||||
|
|
||||||
expect(request).toBeDefined();
|
|
||||||
|
|
||||||
httpRequests.splice(httpRequests.indexOf(request), 1);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .icons.pre {
|
||||||
|
min-width: 22px
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.readonly > .container > .icons.post {
|
&.readonly > .container > .icons.post {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default class Check extends Toggle {
|
||||||
|
|
||||||
set tripleState(value) {
|
set tripleState(value) {
|
||||||
this._tripleState = value;
|
this._tripleState = value;
|
||||||
this.field = this.field;
|
this.field = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get tripleState() {
|
get tripleState() {
|
||||||
|
|
|
@ -45,8 +45,8 @@ describe('Component vnCheck', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should set value to null and change to true when clicked`, () => {
|
it(`should set value to null and change to true when clicked`, () => {
|
||||||
controller.field = null;
|
|
||||||
controller.tripleState = true;
|
controller.tripleState = true;
|
||||||
|
controller.field = null;
|
||||||
element.click();
|
element.click();
|
||||||
|
|
||||||
expect(controller.field).toEqual(true);
|
expect(controller.field).toEqual(true);
|
||||||
|
|
|
@ -58,6 +58,29 @@ vn-chip {
|
||||||
background-color: $color-font-bg-dark;
|
background-color: $color-font-bg-dark;
|
||||||
color: $color-font-bg;
|
color: $color-font-bg;
|
||||||
}
|
}
|
||||||
|
&.pink,
|
||||||
|
&.pink.clickable:hover,
|
||||||
|
&.pink.clickable:focus {
|
||||||
|
background-color: $color-pink;
|
||||||
|
}
|
||||||
|
&.dark-notice,
|
||||||
|
&.dark-notice.clickable:hover,
|
||||||
|
&.dark-notice.clickable:focus {
|
||||||
|
background-color: $color-notice;
|
||||||
|
}
|
||||||
|
&.yellow,
|
||||||
|
&.yellow.clickable:hover,
|
||||||
|
&.yellow.clickable:focus {
|
||||||
|
background-color: $color-yellow;
|
||||||
|
}
|
||||||
|
&.none,
|
||||||
|
&.none.clickable:hover,
|
||||||
|
&.none.clickable:focus {
|
||||||
|
background-color: $color-bg-panel;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid $color-font-link
|
||||||
|
|
||||||
|
}
|
||||||
&.clickable {
|
&.clickable {
|
||||||
@extend %clickable;
|
@extend %clickable;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<span class="required">*</span>
|
<span class="required">*</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="icons pre">
|
<div class="icons pre" ng-class="{'clearable': $ctrl.clearDisabled != true}">
|
||||||
<vn-icon ng-show="::$ctrl.clearDisabled != true"
|
<vn-icon ng-show="::$ctrl.clearDisabled != true"
|
||||||
icon="clear"
|
icon="clear"
|
||||||
translate-attr="{title: 'Clear'}"
|
translate-attr="{title: 'Clear'}"
|
||||||
|
|
|
@ -151,6 +151,9 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
& > .icons.pre.clearable {
|
||||||
|
min-width: 22px
|
||||||
|
}
|
||||||
& > .underline {
|
& > .underline {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
|
@ -436,6 +436,7 @@ export default class SmartTable extends Component {
|
||||||
|
|
||||||
if (filters && filters.userFilter)
|
if (filters && filters.userFilter)
|
||||||
this.model.userFilter = filters.userFilter;
|
this.model.userFilter = filters.userFilter;
|
||||||
|
|
||||||
this.addFilter(field, this.$inputsScope.searchProps[field]);
|
this.addFilter(field, this.$inputsScope.searchProps[field]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,7 +452,7 @@ export default class SmartTable extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilter(field, value) {
|
addFilter(field, value) {
|
||||||
if (value == '') value = null;
|
if (value === '') value = null;
|
||||||
|
|
||||||
let stateFilter = {tableQ: {}};
|
let stateFilter = {tableQ: {}};
|
||||||
if (this.$params.q) {
|
if (this.$params.q) {
|
||||||
|
@ -462,7 +463,7 @@ export default class SmartTable extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereParams = {[field]: value};
|
const whereParams = {[field]: value};
|
||||||
if (value) {
|
if (value !== '' && value !== null && value !== undefined) {
|
||||||
let where = {[field]: value};
|
let where = {[field]: value};
|
||||||
if (this.exprBuilder) {
|
if (this.exprBuilder) {
|
||||||
where = buildFilter(whereParams, (param, value) =>
|
where = buildFilter(whereParams, (param, value) =>
|
||||||
|
|
|
@ -23,6 +23,7 @@ There is a new version, click here to reload: Hay una nueva versión, pulse aqu
|
||||||
|
|
||||||
Back: Volver
|
Back: Volver
|
||||||
Save: Guardar
|
Save: Guardar
|
||||||
|
Assign: Asignar
|
||||||
Create: Crear
|
Create: Crear
|
||||||
Send: Enviar
|
Send: Enviar
|
||||||
Delete: Eliminar
|
Delete: Eliminar
|
||||||
|
|
|
@ -147,8 +147,10 @@
|
||||||
"Receipt's bank was not found": "Receipt's bank was not found",
|
"Receipt's bank was not found": "Receipt's bank was not found",
|
||||||
"This receipt was not compensated": "This receipt was not compensated",
|
"This receipt was not compensated": "This receipt was not compensated",
|
||||||
"Client's email was not found": "Client's email was not found",
|
"Client's email was not found": "Client's email was not found",
|
||||||
|
"Tickets with associated refunds": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº {{id}}",
|
||||||
"It is not possible to modify tracked sales": "It is not possible to modify tracked sales",
|
"It is not possible to modify tracked sales": "It is not possible to modify tracked sales",
|
||||||
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
|
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
|
||||||
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
|
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
|
||||||
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3"
|
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
|
||||||
|
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2"
|
||||||
}
|
}
|
|
@ -263,6 +263,8 @@
|
||||||
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
||||||
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
||||||
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
||||||
"There is no assigned email for this client": "No hay correo asignado para este cliente"
|
"There is no assigned email for this client": "No hay correo asignado para este cliente",
|
||||||
|
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
|
||||||
|
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
|
||||||
|
"Not exist this branch": "La rama no existe"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('isEditable', {
|
Self.remoteMethodCtx('isEditable', {
|
||||||
description: 'Check if a claim is editable',
|
description: 'Check if an state is editable',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'the claim id',
|
description: 'the state id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -21,25 +21,18 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.isEditable = async(ctx, id, options) => {
|
Self.isEditable = async(ctx, id, options) => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const isClaimManager = await Self.app.models.Account.hasRole(userId, 'claimManager', myOptions);
|
const state = await models.ClaimState.findById(id, {
|
||||||
|
include: {
|
||||||
const claim = await Self.app.models.Claim.findById(id, {
|
relation: 'writeRole'
|
||||||
fields: ['claimStateFk'],
|
}
|
||||||
include: [{
|
|
||||||
relation: 'claimState'
|
|
||||||
}]
|
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
const roleWithGrants = state && state.writeRole().name;
|
||||||
const isClaimResolved = claim && claim.claimState().code == 'resolved';
|
return await models.Account.hasRole(userId, roleWithGrants, myOptions);
|
||||||
|
|
||||||
if (!claim || (isClaimResolved && !isClaimManager))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
|
@ -1,16 +1,16 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('claim isEditable()', () => {
|
describe('claimstate isEditable()', () => {
|
||||||
const salesPerdonId = 18;
|
const salesPersonId = 18;
|
||||||
const claimManagerId = 72;
|
const claimManagerId = 72;
|
||||||
it('should return false if the given claim does not exist', async() => {
|
it('should return false if the given state does not exist', async() => {
|
||||||
const tx = await app.models.Claim.beginTransaction({});
|
const tx = await app.models.Claim.beginTransaction({});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: claimManagerId}}};
|
const ctx = {req: {accessToken: {userId: claimManagerId}}};
|
||||||
const result = await app.models.Claim.isEditable(ctx, 99999, options);
|
const result = await app.models.ClaimState.isEditable(ctx, 9999, options);
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
expect(result).toEqual(false);
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ describe('claim isEditable()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: salesPerdonId}}};
|
const ctx = {req: {accessToken: {userId: salesPersonId}}};
|
||||||
const result = await app.models.Claim.isEditable(ctx, 4, options);
|
const result = await app.models.ClaimState.isEditable(ctx, 3, options);
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
expect(result).toEqual(false);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ describe('claim isEditable()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: claimManagerId}}};
|
const ctx = {req: {accessToken: {userId: claimManagerId}}};
|
||||||
const result = await app.models.Claim.isEditable(ctx, 4, options);
|
const result = await app.models.ClaimState.isEditable(ctx, 3, options);
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
expect(result).toEqual(true);
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ describe('claim isEditable()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: salesPerdonId}}};
|
const ctx = {req: {accessToken: {userId: claimManagerId}}};
|
||||||
const result = await app.models.Claim.isEditable(ctx, 1, options);
|
const result = await app.models.ClaimState.isEditable(ctx, 7, options);
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
expect(result).toEqual(true);
|
||||||
|
|
|
@ -65,7 +65,8 @@ module.exports = Self => {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(Self.app.models.Claim.find(filter, myOptions));
|
const models = Self.app.models;
|
||||||
|
promises.push(models.Claim.find(filter, myOptions));
|
||||||
|
|
||||||
// Claim detail
|
// Claim detail
|
||||||
filter = {
|
filter = {
|
||||||
|
@ -82,7 +83,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
promises.push(Self.app.models.ClaimBeginning.find(filter, myOptions));
|
promises.push(models.ClaimBeginning.find(filter, myOptions));
|
||||||
|
|
||||||
// Claim observations
|
// Claim observations
|
||||||
filter = {
|
filter = {
|
||||||
|
@ -96,7 +97,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
promises.push(Self.app.models.ClaimObservation.find(filter, myOptions));
|
promises.push(models.ClaimObservation.find(filter, myOptions));
|
||||||
|
|
||||||
// Claim developments
|
// Claim developments
|
||||||
filter = {
|
filter = {
|
||||||
|
@ -128,7 +129,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
promises.push(Self.app.models.ClaimDevelopment.find(filter, myOptions));
|
promises.push(models.ClaimDevelopment.find(filter, myOptions));
|
||||||
|
|
||||||
// Claim action
|
// Claim action
|
||||||
filter = {
|
filter = {
|
||||||
|
@ -145,11 +146,11 @@ module.exports = Self => {
|
||||||
{relation: 'claimBeggining'}
|
{relation: 'claimBeggining'}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
promises.push(Self.app.models.ClaimEnd.find(filter, myOptions));
|
promises.push(models.ClaimEnd.find(filter, myOptions));
|
||||||
|
|
||||||
const res = await Promise.all(promises);
|
const res = await Promise.all(promises);
|
||||||
|
|
||||||
summary.isEditable = await Self.isEditable(ctx, id, myOptions);
|
summary.isEditable = await models.ClaimState.isEditable(ctx, res[0][0].claimStateFk, myOptions);
|
||||||
[summary.claim] = res[0];
|
[summary.claim] = res[0];
|
||||||
summary.salesClaimed = res[1];
|
summary.salesClaimed = res[1];
|
||||||
summary.observations = res[2];
|
summary.observations = res[2];
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||||
|
const {mergeFilters, mergeWhere} = require('vn-loopback/util/filter');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('logs', {
|
||||||
|
description: 'Find all claim logs of the claim entity matched by a filter',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'The claim id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
http: {source: 'query'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'search',
|
||||||
|
type: 'string',
|
||||||
|
http: {source: 'query'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'userFk',
|
||||||
|
type: 'number',
|
||||||
|
http: {source: 'query'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'created',
|
||||||
|
type: 'date',
|
||||||
|
http: {source: 'query'}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/logs`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.logs = async(ctx, id, filter, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
let to;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
let where = buildFilter(args, (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'search':
|
||||||
|
return {
|
||||||
|
or: [
|
||||||
|
{changedModel: {like: `%${value}%`}},
|
||||||
|
{oldInstance: {like: `%${value}%`}}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
case 'userFk':
|
||||||
|
return {'cl.userFk': value};
|
||||||
|
case 'created':
|
||||||
|
value.setHours(0, 0, 0, 0);
|
||||||
|
to = new Date(value);
|
||||||
|
to.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
|
return {creationDate: {between: [value, to]}};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
where = mergeWhere(where, {['cl.originFk']: id});
|
||||||
|
filter = mergeFilters(args.filter, {where});
|
||||||
|
|
||||||
|
const stmts = [];
|
||||||
|
|
||||||
|
const stmt = new ParameterizedSQL(
|
||||||
|
`SELECT
|
||||||
|
cl.id,
|
||||||
|
cl.userFk,
|
||||||
|
u.name AS userName,
|
||||||
|
cl.oldInstance,
|
||||||
|
cl.newInstance,
|
||||||
|
cl.changedModel,
|
||||||
|
cl.action,
|
||||||
|
cl.creationDate AS created
|
||||||
|
FROM claimLog cl
|
||||||
|
JOIN account.user u ON u.id = cl.userFk
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
const result = await conn.executeStmt(sql, myOptions);
|
||||||
|
|
||||||
|
const logs = [];
|
||||||
|
for (const row of result) {
|
||||||
|
const changes = [];
|
||||||
|
const oldInstance = JSON.parse(row.oldInstance);
|
||||||
|
const newInstance = JSON.parse(row.newInstance);
|
||||||
|
const mergedProperties = [...Object.keys(oldInstance), ...Object.keys(newInstance)];
|
||||||
|
const properties = new Set(mergedProperties);
|
||||||
|
for (const property of properties) {
|
||||||
|
let oldValue = oldInstance[property];
|
||||||
|
let newValue = newInstance[property];
|
||||||
|
|
||||||
|
const change = {
|
||||||
|
property: property,
|
||||||
|
before: oldValue,
|
||||||
|
after: newValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
changes.push(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
logs.push({
|
||||||
|
model: row.changedModel,
|
||||||
|
action: row.action,
|
||||||
|
created: row.created,
|
||||||
|
userFk: row.userFk,
|
||||||
|
userName: row.userName,
|
||||||
|
changes: changes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('claim log()', () => {
|
||||||
|
const claimId = 1;
|
||||||
|
const salesPersonId = 18;
|
||||||
|
|
||||||
|
it('should return results filtering by user id', async() => {
|
||||||
|
const result = await app.models.Claim.logs({args: {userFk: salesPersonId}}, claimId);
|
||||||
|
|
||||||
|
const expectedObject = {
|
||||||
|
model: 'Claim',
|
||||||
|
action: 'update',
|
||||||
|
changes: [
|
||||||
|
{property: 'hasToPickUp', before: false, after: true}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const firstRow = result[0];
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
expect(firstRow).toEqual(jasmine.objectContaining(expectedObject));
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,6 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('updateClaim', {
|
Self.remoteMethod('updateClaim', {
|
||||||
description: 'Update a claim with privileges',
|
description: 'Update a claim with privileges',
|
||||||
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
arg: 'ctx',
|
arg: 'ctx',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -78,11 +79,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
// Validate when claimState has been changed
|
// Validate when claimState has been changed
|
||||||
if (args.claimStateFk) {
|
if (args.claimStateFk) {
|
||||||
const canUpdate = await canChangeState(ctx, claim.claimStateFk, myOptions);
|
const canEditOldState = await models.ClaimState.isEditable(ctx, claim.claimStateFk, myOptions);
|
||||||
const hasRights = await canChangeState(ctx, args.claimStateFk, myOptions);
|
const canEditNewState = await models.ClaimState.isEditable(ctx, args.claimStateFk, myOptions);
|
||||||
const isClaimManager = await models.Account.hasRole(userId, 'claimManager', myOptions);
|
const isClaimManager = await models.Account.hasRole(userId, 'claimManager', myOptions);
|
||||||
|
|
||||||
if (!canUpdate || !hasRights || changedHasToPickUp && !isClaimManager)
|
if (!canEditOldState || !canEditNewState || changedHasToPickUp && !isClaimManager)
|
||||||
throw new UserError(`You don't have enough privileges to change that field`);
|
throw new UserError(`You don't have enough privileges to change that field`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,21 +114,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function canChangeState(ctx, id, options) {
|
|
||||||
let models = Self.app.models;
|
|
||||||
let userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
let state = await models.ClaimState.findById(id, {
|
|
||||||
include: {
|
|
||||||
relation: 'writeRole'
|
|
||||||
}
|
|
||||||
}, options);
|
|
||||||
let stateRole = state.writeRole().name;
|
|
||||||
let canUpdate = await models.Account.hasRole(userId, stateRole, options);
|
|
||||||
|
|
||||||
return canUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function notifyStateChange(ctx, workerId, claim, state) {
|
async function notifyStateChange(ctx, workerId, claim, state) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const origin = ctx.req.headers.origin;
|
const origin = ctx.req.headers.origin;
|
||||||
|
|
|
@ -11,7 +11,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.observe('before save', async ctx => {
|
Self.observe('before save', async ctx => {
|
||||||
if (ctx.isNewInstance) return;
|
if (ctx.isNewInstance) return;
|
||||||
await claimIsEditable(ctx);
|
//await claimIsEditable(ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('before delete', async ctx => {
|
Self.observe('before delete', async ctx => {
|
||||||
|
@ -22,8 +22,28 @@ module.exports = Self => {
|
||||||
async function claimIsEditable(ctx) {
|
async function claimIsEditable(ctx) {
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
const httpCtx = {req: loopBackContext.active};
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (ctx.options && ctx.options.transaction)
|
||||||
|
myOptions.transaction = ctx.options.transaction;
|
||||||
|
|
||||||
const claimBeginning = await Self.findById(ctx.where.id);
|
const claimBeginning = await Self.findById(ctx.where.id);
|
||||||
const isEditable = await Self.app.models.Claim.isEditable(httpCtx, claimBeginning.claimFk);
|
|
||||||
|
const filter = {
|
||||||
|
where: {id: claimBeginning.claimFk},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'claimState',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'code', 'description']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const [claim] = await models.Claim.find(filter, myOptions);
|
||||||
|
const isEditable = await models.ClaimState.isEditable(httpCtx, claim.claimState().id);
|
||||||
|
|
||||||
if (!isEditable)
|
if (!isEditable)
|
||||||
throw new UserError(`The current claim can't be modified`);
|
throw new UserError(`The current claim can't be modified`);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/claim-state/isEditable')(Self);
|
||||||
|
};
|
|
@ -6,9 +6,9 @@ module.exports = Self => {
|
||||||
require('../methods/claim/regularizeClaim')(Self);
|
require('../methods/claim/regularizeClaim')(Self);
|
||||||
require('../methods/claim/uploadFile')(Self);
|
require('../methods/claim/uploadFile')(Self);
|
||||||
require('../methods/claim/updateClaimAction')(Self);
|
require('../methods/claim/updateClaimAction')(Self);
|
||||||
require('../methods/claim/isEditable')(Self);
|
|
||||||
require('../methods/claim/updateClaimDestination')(Self);
|
require('../methods/claim/updateClaimDestination')(Self);
|
||||||
require('../methods/claim/downloadFile')(Self);
|
require('../methods/claim/downloadFile')(Self);
|
||||||
require('../methods/claim/claimPickupPdf')(Self);
|
require('../methods/claim/claimPickupPdf')(Self);
|
||||||
require('../methods/claim/claimPickupEmail')(Self);
|
require('../methods/claim/claimPickupEmail')(Self);
|
||||||
|
require('../methods/claim/logs')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,8 +100,8 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
setClaimedQuantity(id, claimedQuantity) {
|
setClaimedQuantity(id, claimedQuantity) {
|
||||||
let params = {id: id, quantity: claimedQuantity};
|
let params = {quantity: claimedQuantity};
|
||||||
let query = `ClaimBeginnings/`;
|
let query = `ClaimBeginnings/${id}`;
|
||||||
this.$http.patch(query, params).then(() => {
|
this.$http.patch(query, params).then(() => {
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
this.calculateTotals();
|
this.calculateTotals();
|
||||||
|
@ -151,7 +151,7 @@ class Controller extends Section {
|
||||||
isClaimEditable() {
|
isClaimEditable() {
|
||||||
if (!this.claim) return;
|
if (!this.claim) return;
|
||||||
|
|
||||||
this.$http.get(`Claims/${this.claim.id}/isEditable`).then(res => {
|
this.$http.get(`ClaimStates/${this.claim.id}/isEditable`).then(res => {
|
||||||
this.isRewritable = res.data;
|
this.isRewritable = res.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe('claim', () => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$httpBackend.whenGET('Claims/ClaimBeginnings').respond({});
|
$httpBackend.whenGET('Claims/ClaimBeginnings').respond({});
|
||||||
$httpBackend.whenGET(`Tickets/1/isEditable`).respond(true);
|
$httpBackend.whenGET(`Tickets/1/isEditable`).respond(true);
|
||||||
$httpBackend.whenGET(`Claims/2/isEditable`).respond(true);
|
$httpBackend.whenGET(`ClaimStates/2/isEditable`).respond(true);
|
||||||
const $element = angular.element('<vn-claim-detail></vn-claim-detail>');
|
const $element = angular.element('<vn-claim-detail></vn-claim-detail>');
|
||||||
controller = $componentController('vnClaimDetail', {$element, $scope});
|
controller = $componentController('vnClaimDetail', {$element, $scope});
|
||||||
controller.claim = {
|
controller.claim = {
|
||||||
|
@ -89,9 +89,12 @@ describe('claim', () => {
|
||||||
|
|
||||||
describe('setClaimedQuantity(id, claimedQuantity)', () => {
|
describe('setClaimedQuantity(id, claimedQuantity)', () => {
|
||||||
it('should make a patch and call refresh and showSuccess', () => {
|
it('should make a patch and call refresh and showSuccess', () => {
|
||||||
|
const id = 1;
|
||||||
|
const claimedQuantity = 1;
|
||||||
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
$httpBackend.expectPATCH(`ClaimBeginnings/`).respond({});
|
$httpBackend.expectPATCH(`ClaimBeginnings/${id}`).respond({});
|
||||||
controller.setClaimedQuantity(1, 1);
|
controller.setClaimedQuantity(id, claimedQuantity);
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
|
|
@ -44,7 +44,7 @@ module.exports = Self => {
|
||||||
try {
|
try {
|
||||||
const invoiceOut = await Self.findById(id, null, myOptions);
|
const invoiceOut = await Self.findById(id, null, myOptions);
|
||||||
const hasInvoicing = await models.Account.hasRole(userId, 'invoicing', myOptions);
|
const hasInvoicing = await models.Account.hasRole(userId, 'invoicing', myOptions);
|
||||||
|
console.log(invoiceOut, !hasInvoicing);
|
||||||
if (invoiceOut.hasPdf && !hasInvoicing)
|
if (invoiceOut.hasPdf && !hasInvoicing)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ module.exports = Self => {
|
||||||
ids = ids.split(',');
|
ids = ids.split(',');
|
||||||
|
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
|
console.log(zipConfig, totalSize, zipConfig ? zipConfig.maxSize : null);
|
||||||
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large');
|
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large');
|
||||||
const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions);
|
const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions);
|
||||||
const fileName = extractFileName(invoiceOutPdf[2]);
|
const fileName = extractFileName(invoiceOutPdf[2]);
|
||||||
|
|
|
@ -11,7 +11,6 @@ describe('InvoiceOut createPdf()', () => {
|
||||||
const ctx = {req: activeCtx};
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
it('should create a new PDF file and set true the hasPdf property', async() => {
|
it('should create a new PDF file and set true the hasPdf property', async() => {
|
||||||
pending('https://redmine.verdnatura.es/issues/5035');
|
|
||||||
const invoiceId = 1;
|
const invoiceId = 1;
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
active: activeCtx
|
active: activeCtx
|
||||||
|
|
|
@ -30,8 +30,6 @@ describe('InvoiceOut downloadZip()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an error if the size of the files is too large', async() => {
|
it('should return an error if the size of the files is too large', async() => {
|
||||||
pending('https://redmine.verdnatura.es/issues/5035');
|
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
|
|
@ -65,8 +65,9 @@ describe('InvoiceOut filter()', () => {
|
||||||
await invoiceOut.updateAttribute('hasPdf', true, options);
|
await invoiceOut.updateAttribute('hasPdf', true, options);
|
||||||
|
|
||||||
const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options);
|
const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options);
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
expect(result.length).toBeGreaterThanOrEqual(1);
|
expect(result.length).toEqual(1);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ module.exports = Self => {
|
||||||
const itemType = await models.ItemType.findById(params.typeFk, {fields: ['isLaid']}, myOptions);
|
const itemType = await models.ItemType.findById(params.typeFk, {fields: ['isLaid']}, myOptions);
|
||||||
|
|
||||||
params.isLaid = itemType.isLaid;
|
params.isLaid = itemType.isLaid;
|
||||||
|
params.isPhotoRequested = true;
|
||||||
|
|
||||||
const item = await models.Item.create(params, myOptions);
|
const item = await models.Item.create(params, myOptions);
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,12 @@
|
||||||
},
|
},
|
||||||
"isLaid": {
|
"isLaid": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"isPhotoRequested": {
|
||||||
|
"type": "boolean",
|
||||||
|
"mysql":{
|
||||||
|
"columnName": "doPhoto"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -182,6 +182,12 @@
|
||||||
ng-model="$ctrl.item.isFragile"
|
ng-model="$ctrl.item.isFragile"
|
||||||
info="Is shown at website, app that this item cannot travel (wreath, palms, ...)">
|
info="Is shown at website, app that this item cannot travel (wreath, palms, ...)">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="Do photo"
|
||||||
|
ng-model="$ctrl.item.isPhotoRequested"
|
||||||
|
info="This item does need a photo">
|
||||||
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
|
|
|
@ -12,3 +12,5 @@ Fragile: Frágil
|
||||||
Is shown at website, app that this item cannot travel (wreath, palms, ...): Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
|
Is shown at website, app that this item cannot travel (wreath, palms, ...): Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
|
||||||
Multiplier: Multiplicador
|
Multiplier: Multiplicador
|
||||||
Generic: Genérico
|
Generic: Genérico
|
||||||
|
This item does need a photo: Este artículo necesita una foto
|
||||||
|
Do photo: Hacer foto
|
|
@ -91,6 +91,9 @@ class Controller extends Descriptor {
|
||||||
|
|
||||||
this.$.photo.setAttribute('src', newSrc);
|
this.$.photo.setAttribute('src', newSrc);
|
||||||
this.$.photo.setAttribute('zoom-image', newZoomSrc);
|
this.$.photo.setAttribute('zoom-image', newZoomSrc);
|
||||||
|
|
||||||
|
if (this.item.isPhotoRequested)
|
||||||
|
this.$http.patch(`Items/${this.item.id}`, {isPhotoRequested: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
getWarehouseName(warehouseFk) {
|
getWarehouseName(warehouseFk) {
|
||||||
|
|
|
@ -8,7 +8,8 @@ describe('vnItemDescriptor', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
itemType: {
|
itemType: {
|
||||||
warehouseFk: 1
|
warehouseFk: 1
|
||||||
}
|
},
|
||||||
|
isPhotoRequested: true
|
||||||
};
|
};
|
||||||
const stock = {
|
const stock = {
|
||||||
visible: 1,
|
visible: 1,
|
||||||
|
@ -43,4 +44,16 @@ describe('vnItemDescriptor', () => {
|
||||||
expect(controller.item).toEqual(item);
|
expect(controller.item).toEqual(item);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('onUploadResponse()', () => {
|
||||||
|
it(`should change isPhotoRequested when a new photo is uploaded`, () => {
|
||||||
|
controller.item = item;
|
||||||
|
controller.$rootScope = {imagePath: () => {}};
|
||||||
|
controller.$.photo = {setAttribute: () => {}};
|
||||||
|
|
||||||
|
$httpBackend.expectPATCH(`Items/${item.id}`).respond(200);
|
||||||
|
controller.onUploadResponse();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,6 +70,7 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
$onDestroy() {
|
$onDestroy() {
|
||||||
|
if (this.$state.getCurrentPath()[2].state.name === 'item')
|
||||||
this.card.reload();
|
this.card.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,15 @@
|
||||||
{{$ctrl.summary.item.itemType.worker.user.name}}
|
{{$ctrl.summary.item.itemType.worker.user.name}}
|
||||||
</span>
|
</span>
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-check
|
||||||
|
label="Do photo"
|
||||||
|
disabled="true"
|
||||||
|
vn-one
|
||||||
|
ng-model="$ctrl.item.isPhotoRequested"
|
||||||
|
info="This item does need a photo">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one name="otherData">
|
<vn-one name="otherData">
|
||||||
<h4 ng-show="$ctrl.isBuyer">
|
<h4 ng-show="$ctrl.isBuyer">
|
||||||
|
|
|
@ -33,16 +33,16 @@
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
<vn-tr ng-repeat="waste in detail.lines" class="clickable vn-tr"
|
<a ng-repeat="waste in detail.lines" class="clickable vn-tr"
|
||||||
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})"
|
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})"
|
||||||
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}">
|
ng-show="$ctrl.wasteConfig[detail.buyer].hidden">
|
||||||
<vn-td></vn-td>
|
<vn-td></vn-td>
|
||||||
<vn-td>{{::waste.family}}</vn-td>
|
<vn-td>{{::waste.family}}</vn-td>
|
||||||
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</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.dwindle | currency: 'EUR'}}</vn-td>
|
||||||
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
|
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
|
||||||
<vn-td shrink></vn-td>
|
<vn-td shrink></vn-td>
|
||||||
</vn-tr>
|
</a>
|
||||||
</vn-tbody>
|
</vn-tbody>
|
||||||
</vn-table>
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -47,7 +47,16 @@ module.exports = Self => {
|
||||||
verb: 'POST'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Self.upload = async(ctx, options) => {
|
Self.upload = async(
|
||||||
|
ctx,
|
||||||
|
appName,
|
||||||
|
toVersion,
|
||||||
|
branch,
|
||||||
|
fromVersion,
|
||||||
|
description,
|
||||||
|
unlock,
|
||||||
|
options
|
||||||
|
) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
@ -55,12 +64,6 @@ module.exports = Self => {
|
||||||
const AccessContainer = models.AccessContainer;
|
const AccessContainer = models.AccessContainer;
|
||||||
const fileOptions = {};
|
const fileOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
const appName = ctx.args.appName;
|
|
||||||
const toVersion = ctx.args.toVersion;
|
|
||||||
const branch = ctx.args.branch;
|
|
||||||
const fromVersion = ctx.args.fromVersion;
|
|
||||||
let description = ctx.args.description;
|
|
||||||
const unlock = ctx.args.unlock;
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
@ -153,11 +156,14 @@ module.exports = Self => {
|
||||||
formatDesc += `*${appName.toUpperCase()}* v.${toVersion} `;
|
formatDesc += `*${appName.toUpperCase()}* v.${toVersion} `;
|
||||||
|
|
||||||
const oldVersion = await models.MdbVersionTree.findOne({
|
const oldVersion = await models.MdbVersionTree.findOne({
|
||||||
where: {version: fromVersion},
|
where: {
|
||||||
|
version: fromVersion,
|
||||||
|
app: appName
|
||||||
|
},
|
||||||
fields: ['branchFk']
|
fields: ['branchFk']
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
if (branch == oldVersion.branchFk)
|
if (!oldVersion || branch == oldVersion.branchFk)
|
||||||
formatDesc += `[*${branch}*]: `;
|
formatDesc += `[*${branch}*]: `;
|
||||||
else
|
else
|
||||||
formatDesc += `[*${oldVersion.branchFk}* » *${branch}*]: `;
|
formatDesc += `[*${oldVersion.branchFk}* » *${branch}*]: `;
|
||||||
|
|
|
@ -93,7 +93,14 @@
|
||||||
<vn-tr ng-repeat="ticket in $ctrl.summary.tickets">
|
<vn-tr ng-repeat="ticket in $ctrl.summary.tickets">
|
||||||
<vn-td shrink>{{ticket.priority | dashIfEmpty}}</vn-td>
|
<vn-td shrink>{{ticket.priority | dashIfEmpty}}</vn-td>
|
||||||
<vn-td expand title="{{ticket.address.street}}">{{ticket.street}}</vn-td>
|
<vn-td expand title="{{ticket.address.street}}">{{ticket.street}}</vn-td>
|
||||||
<vn-td expand>{{ticket.city}}</vn-td>
|
<vn-td
|
||||||
|
expand
|
||||||
|
ng-click="$ctrl.goToBuscaman(ticket)"
|
||||||
|
class="link"
|
||||||
|
vn-tooltip="Open buscaman"
|
||||||
|
tooltip-position="up">
|
||||||
|
{{::ticket.city}}
|
||||||
|
</vn-td>
|
||||||
<vn-td shrink>{{ticket.postalCode}}</vn-td>
|
<vn-td shrink>{{ticket.postalCode}}</vn-td>
|
||||||
<vn-td>
|
<vn-td>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -31,6 +31,21 @@ class Controller extends Summary {
|
||||||
this.sumPackages();
|
this.sumPackages();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goToBuscaman(ticket) {
|
||||||
|
if (!this.route.vehicleFk)
|
||||||
|
throw new UserError(`The route doesn't have a vehicle`);
|
||||||
|
let query = `Routes/${this.route.vehicleFk}/getDeliveryPoint`;
|
||||||
|
|
||||||
|
this.$http.get(query).then(response => {
|
||||||
|
if (!response.data)
|
||||||
|
throw new UserError(`The route's vehicle doesn't have a delivery point`);
|
||||||
|
|
||||||
|
const address = response.data + '+to:' + ticket.postalCode + ' ' + ticket.city + ' ' + ticket.street;
|
||||||
|
const url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
|
||||||
|
window.open(url + encodeURI(address), '_blank');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnRouteSummary', {
|
ngModule.vnComponent('vnRouteSummary', {
|
||||||
|
|
|
@ -37,5 +37,29 @@ describe('Route', () => {
|
||||||
expect(controller.packagesTotal).toEqual(4);
|
expect(controller.packagesTotal).toEqual(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('goToBuscaman()', () => {
|
||||||
|
it('should open buscaman with the given arguments', () => {
|
||||||
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
|
const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460%20Av%20Espioca%20100+to:n19%20London%20my%20street';
|
||||||
|
controller.route = {vehicleFk: 1};
|
||||||
|
const url = `Routes/${controller.route.vehicleFk}/getDeliveryPoint`;
|
||||||
|
$httpBackend.when('GET', `Routes/1/summary`).respond();
|
||||||
|
$httpBackend.expectGET(url).respond('46460 Av Espioca 100');
|
||||||
|
|
||||||
|
const ticket = {
|
||||||
|
id: 1,
|
||||||
|
checked: true,
|
||||||
|
street: 'my street',
|
||||||
|
postalCode: 'n19',
|
||||||
|
city: 'London'
|
||||||
|
};
|
||||||
|
|
||||||
|
controller.goToBuscaman(ticket);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(expectedUrl, '_blank');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -93,7 +93,14 @@
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td expand title="{{::ticket.street}}">{{::ticket.street}}</vn-td>
|
<vn-td expand title="{{::ticket.street}}">{{::ticket.street}}</vn-td>
|
||||||
<vn-td expand>{{::ticket.city}}</vn-td>
|
<vn-td
|
||||||
|
expand
|
||||||
|
ng-click="$ctrl.goToBuscaman(ticket)"
|
||||||
|
class="link"
|
||||||
|
vn-tooltip="Open buscaman"
|
||||||
|
tooltip-position="up">
|
||||||
|
{{::ticket.city}}
|
||||||
|
</vn-td>
|
||||||
<vn-td shrink>{{::ticket.postalCode}}</vn-td>
|
<vn-td shrink>{{::ticket.postalCode}}</vn-td>
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -74,29 +74,24 @@ class Controller extends Section {
|
||||||
return selectedItems;
|
return selectedItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
goToBuscaman() {
|
goToBuscaman(ticket) {
|
||||||
if (!this.route.vehicleFk)
|
if (!this.route.vehicleFk)
|
||||||
throw new UserError(`The route doesn't have a vehicle`);
|
throw new UserError(`The route doesn't have a vehicle`);
|
||||||
let query = `Routes/${this.route.vehicleFk}/getDeliveryPoint`;
|
|
||||||
|
|
||||||
this.$http.get(query).then(response => {
|
this.$http.get(`Routes/${this.route.vehicleFk}/getDeliveryPoint`).then(res => {
|
||||||
if (!response.data)
|
if (!res.data)
|
||||||
throw new UserError(`The route's vehicle doesn't have a delivery point`);
|
throw new UserError(`The route's vehicle doesn't have a delivery point`);
|
||||||
|
|
||||||
return response.data;
|
let addresses = res.data;
|
||||||
}).then(address => {
|
const lines = ticket ? [ticket] : this.getSelectedItems(this.tickets);
|
||||||
let addresses;
|
|
||||||
if (address) addresses = address;
|
|
||||||
let lines = this.getSelectedItems(this.tickets);
|
|
||||||
|
|
||||||
let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
|
|
||||||
lines.forEach((line, index) => {
|
lines.forEach((line, index) => {
|
||||||
const previusLine = lines[index - 1] ? lines[index - 1].street : null;
|
const previousLine = lines[index - 1] ? lines[index - 1].street : null;
|
||||||
if (previusLine != line.street)
|
if (previousLine != line.street)
|
||||||
addresses = addresses + '+to:' + line.postalCode + ' ' + line.city + ' ' + line.street;
|
addresses = addresses + '+to:' + line.postalCode + ' ' + line.city + ' ' + line.street;
|
||||||
});
|
});
|
||||||
|
|
||||||
window.open(url + addresses, '_blank');
|
const url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
|
||||||
|
window.open(url + encodeURI(addresses), '_blank');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ describe('Route', () => {
|
||||||
describe('goToBuscaman()', () => {
|
describe('goToBuscaman()', () => {
|
||||||
it('should open buscaman with the given arguments', () => {
|
it('should open buscaman with the given arguments', () => {
|
||||||
jest.spyOn(window, 'open').mockReturnThis();
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460 Av Espioca 100+to:n19 London my street';
|
const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460%20Av%20Espioca%20100+to:n19%20London%20my%20street';
|
||||||
controller.route = {vehicleFk: 1};
|
controller.route = {vehicleFk: 1};
|
||||||
const url = `Routes/${controller.route.vehicleFk}/getDeliveryPoint`;
|
const url = `Routes/${controller.route.vehicleFk}/getDeliveryPoint`;
|
||||||
$httpBackend.expectGET(url).respond('46460 Av Espioca 100');
|
$httpBackend.expectGET(url).respond('46460 Av Espioca 100');
|
||||||
|
|
|
@ -64,6 +64,14 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'healthRegister',
|
arg: 'healthRegister',
|
||||||
type: 'string'
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'isVies',
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'isTrucker',
|
||||||
|
type: 'boolean'
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
arg: 'res',
|
arg: 'res',
|
||||||
|
|
|
@ -110,6 +110,9 @@
|
||||||
},
|
},
|
||||||
"healthRegister": {
|
"healthRegister": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isVies": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -172,7 +172,7 @@
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-id="country" vn-one
|
<vn-autocomplete vn-id="country" vn-two
|
||||||
ng-model="$ctrl.supplier.countryFk"
|
ng-model="$ctrl.supplier.countryFk"
|
||||||
data="countries"
|
data="countries"
|
||||||
show-field="country"
|
show-field="country"
|
||||||
|
@ -180,10 +180,17 @@
|
||||||
label="Country"
|
label="Country"
|
||||||
rule>
|
rule>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-two class="vn-pl-xs">
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Trucker"
|
label="Trucker"
|
||||||
ng-model="$ctrl.supplier.isTrucker">
|
ng-model="$ctrl.supplier.isTrucker">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
class="vn-ml-lg"
|
||||||
|
label="Vies"
|
||||||
|
ng-model="$ctrl.supplier.isVies">
|
||||||
|
</vn-check>
|
||||||
|
</vn-two>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
|
|
|
@ -111,7 +111,7 @@ module.exports = Self => {
|
||||||
args.landed = landedResult && landedResult.landed;
|
args.landed = landedResult && landedResult.landed;
|
||||||
}
|
}
|
||||||
|
|
||||||
query = `CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
|
query = `CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
|
||||||
SELECT @result newTicketId;`;
|
SELECT @result newTicketId;`;
|
||||||
const result = await Self.rawSql(query, [
|
const result = await Self.rawSql(query, [
|
||||||
args.clientId,
|
args.clientId,
|
||||||
|
@ -122,6 +122,7 @@ module.exports = Self => {
|
||||||
args.agencyModeId || null,
|
args.agencyModeId || null,
|
||||||
args.routeId || null,
|
args.routeId || null,
|
||||||
args.landed,
|
args.landed,
|
||||||
|
true,
|
||||||
myUserId
|
myUserId
|
||||||
], myOptions);
|
], myOptions);
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,14 @@ module.exports = Self => {
|
||||||
if (!isEditable)
|
if (!isEditable)
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
|
// Check if ticket has refunds
|
||||||
|
const ticketRefunds = await models.TicketRefund.find({
|
||||||
|
where: {originalTicketFk: id},
|
||||||
|
fields: ['id']}
|
||||||
|
, myOptions);
|
||||||
|
if (ticketRefunds.length > 0)
|
||||||
|
throw new UserError($t('Tickets with associated refunds', {id: ticketRefunds[0].id}));
|
||||||
|
|
||||||
// Check if has sales with shelving
|
// Check if has sales with shelving
|
||||||
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||||
const sales = await models.Sale.find({
|
const sales = await models.Sale.find({
|
||||||
|
|
|
@ -101,4 +101,32 @@ describe('ticket setDeleted()', () => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show error trying to delete a ticket with a refund', async() => {
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'http://localhost:5000'},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.req.__ = value => {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ticketId = 12;
|
||||||
|
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain('Tickets with associated refunds');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -80,34 +80,6 @@ describe('sale transferSales()', () => {
|
||||||
expect(error.message).toEqual(`Can't transfer claimed sales`);
|
expect(error.message).toEqual(`Can't transfer claimed sales`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to transfer claimed sales if the role is claimManager', async() => {
|
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const claimManagerId = 72;
|
|
||||||
const myActiveCtx = {
|
|
||||||
accessToken: {userId: claimManagerId},
|
|
||||||
};
|
|
||||||
const myCtx = {req: myActiveCtx};
|
|
||||||
|
|
||||||
const ticketId = 11;
|
|
||||||
const receiverTicketId = null;
|
|
||||||
const sales = await models.Ticket.getSales(ticketId, options);
|
|
||||||
|
|
||||||
await models.Ticket.transferSales(myCtx, ticketId, receiverTicketId, sales, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should transfer the sales from a ticket to a new one', async() => {
|
it('should transfer the sales from a ticket to a new one', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
const saleIds = sales.map(sale => sale.id);
|
const saleIds = sales.map(sale => sale.id);
|
||||||
|
|
||||||
const isClaimManager = await models.Account.hasRole(userId, 'claimManager');
|
|
||||||
const hasClaimedSales = await models.ClaimBeginning.findOne({where: {saleFk: {inq: saleIds}}});
|
const hasClaimedSales = await models.ClaimBeginning.findOne({where: {saleFk: {inq: saleIds}}});
|
||||||
if (hasClaimedSales && !isClaimManager)
|
if (hasClaimedSales)
|
||||||
throw new UserError(`Can't transfer claimed sales`);
|
throw new UserError(`Can't transfer claimed sales`);
|
||||||
|
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
|
|
|
@ -24,11 +24,43 @@
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<vn-tr ng-repeat="sale in $ctrl.sales">
|
<vn-tr ng-repeat="sale in $ctrl.sales">
|
||||||
<vn-td center>
|
<vn-td center>
|
||||||
<span class="chip {{$ctrl.chipHasSaleGroupDetail(sale.preparingList.hasSaleGroupDetail)}} vn-mx-xs chip2" vn-tooltip="has saleGroupDetail"></span>
|
<vn-chip
|
||||||
<span class="chip {{$ctrl.chipIsPreviousSelected(sale.preparingList.isPreviousSelected)}} vn-ml-xs" vn-tooltip="is previousSelected"></span>
|
ng-class="::{
|
||||||
<span class="chip {{$ctrl.chipIsPrevious(sale.preparingList.isPrevious)}} vn-mr-xs" vn-tooltip="is previous"></span>
|
'pink': sale.preparingList.hasSaleGroupDetail,
|
||||||
<span class="chip {{$ctrl.chipIsPrepared(sale.preparingList.isPrepared)}} vn-mx-xs" vn-tooltip="is prepared"></span>
|
'none': !sale.preparingList.hasSaleGroupDetail,
|
||||||
<span class="chip {{$ctrl.chipIsControled(sale.preparingList.isControled)}} vn-mx-xs" vn-tooltip="is controled"></span>
|
}"
|
||||||
|
class="circle"
|
||||||
|
vn-tooltip="has saleGroupDetail"
|
||||||
|
>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip ng-class="::{
|
||||||
|
'notice': sale.preparingList.isPreviousSelected,
|
||||||
|
'none': !sale.preparingList.isPreviousSelected,
|
||||||
|
}"
|
||||||
|
class="circle"
|
||||||
|
vn-tooltip="is previousSelected">
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip ng-class="::{
|
||||||
|
'dark-notice': sale.preparingList.isPrevious,
|
||||||
|
'none': !sale.preparingList.isPrevious,
|
||||||
|
}"
|
||||||
|
class="circle"
|
||||||
|
vn-tooltip="is previous">
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip ng-class="::{
|
||||||
|
'warning': sale.preparingList.isPrepared,
|
||||||
|
'none': !sale.preparingList.isPrepared,
|
||||||
|
}"
|
||||||
|
class="circle"
|
||||||
|
vn-tooltip="is prepared">
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip ng-class="::{
|
||||||
|
'yellow': sale.preparingList.isControled,
|
||||||
|
'none': !sale.preparingList.isControled,
|
||||||
|
}"
|
||||||
|
class="circle"
|
||||||
|
vn-tooltip="is controled">
|
||||||
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -74,31 +74,6 @@ class Controller extends Section {
|
||||||
this.$.itemDescriptor.show(event.target, sale.item.id);
|
this.$.itemDescriptor.show(event.target, sale.item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
chipHasSaleGroupDetail(hasSaleGroupDetail) {
|
|
||||||
if (hasSaleGroupDetail) return 'pink';
|
|
||||||
else return 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
chipIsPreviousSelected(isPreviousSelected) {
|
|
||||||
if (isPreviousSelected) return 'notice';
|
|
||||||
else return 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
chipIsPrevious(isPrevious) {
|
|
||||||
if (isPrevious) return 'dark-notice';
|
|
||||||
else return 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
chipIsPrepared(isPrepared) {
|
|
||||||
if (isPrepared) return 'warning';
|
|
||||||
else return 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
chipIsControled(isControled) {
|
|
||||||
if (isControled) return 'yellow';
|
|
||||||
else return 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
showSaleTracking(sale) {
|
showSaleTracking(sale) {
|
||||||
this.saleId = sale.id;
|
this.saleId = sale.id;
|
||||||
this.$.saleTracking.show();
|
this.$.saleTracking.show();
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
.chip {
|
.circle {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 15px;
|
justify-content: center;
|
||||||
min-height: 25px;
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: .9rem;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('allocatePDA', {
|
||||||
|
description: 'Deallocate the PDA of the worker',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'pda',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The pda id'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/allocatePDA`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.allocatePDA = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pda = await models.DeviceProduction.findById(args.pda, myOptions);
|
||||||
|
if (pda.stateFk != 'idle') throw new UserError(`The PDA state is not idle`);
|
||||||
|
await pda.updateAttributes({stateFk: 'active'}, myOptions);
|
||||||
|
await models.DeviceProductionUser.create({
|
||||||
|
deviceProductionFk: args.pda,
|
||||||
|
userFk: args.id,
|
||||||
|
created: new Date()
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return {
|
||||||
|
deviceProductionFk: pda.id,
|
||||||
|
deviceProduction: {
|
||||||
|
modelFk: pda.modelFk,
|
||||||
|
serialNumber: pda.serialNumber
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,53 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deallocatePDA', {
|
||||||
|
description: 'Deallocate the worker\'s PDA',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'pda',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The pda id'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/deallocatePDA`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deallocatePDA = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pda = await models.DeviceProduction.findById(args.pda, myOptions);
|
||||||
|
await pda.updateAttributes({stateFk: 'idle'}, myOptions);
|
||||||
|
await models.DeviceProductionUser.destroyAll({userFk: args.id, deviceProductionFk: args.pda}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return pda;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -13,55 +13,59 @@ module.exports = Self => {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
arg: 'tags',
|
{
|
||||||
type: ['Object'],
|
|
||||||
description: 'List of tags to filter with',
|
|
||||||
http: {source: 'query'}
|
|
||||||
}, {
|
|
||||||
arg: 'search',
|
arg: 'search',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: `If it's and integer searchs by id, otherwise it searchs by name`,
|
description: `If it's and integer searchs by id, otherwise it searchs by name`,
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'Integer',
|
type: 'Integer',
|
||||||
description: 'The worker id',
|
description: 'The worker id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'userFk',
|
arg: 'userFk',
|
||||||
type: 'Integer',
|
type: 'Integer',
|
||||||
description: 'The user id',
|
description: 'The user id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'fi',
|
arg: 'fi',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The worker fi',
|
description: 'The worker fi',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'departmentFk',
|
arg: 'departmentFk',
|
||||||
type: 'Integer',
|
type: 'Integer',
|
||||||
description: 'The worker department id',
|
description: 'The worker department id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'extension',
|
arg: 'extension',
|
||||||
type: 'Integer',
|
type: 'Integer',
|
||||||
description: 'The worker extension id',
|
description: 'The worker extension id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
arg: 'firstName',
|
arg: 'firstName',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The worker firstName',
|
description: 'The worker firstName',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
arg: 'name',
|
{
|
||||||
|
arg: 'lastName',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The worker name',
|
description: 'The worker lastName',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
},
|
||||||
arg: 'nickname',
|
{
|
||||||
|
arg: 'userName',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The worker nickname',
|
description: 'The worker user name',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -93,10 +97,10 @@ module.exports = Self => {
|
||||||
return {'w.id': value};
|
return {'w.id': value};
|
||||||
case 'userFk':
|
case 'userFk':
|
||||||
return {'w.userFk': value};
|
return {'w.userFk': value};
|
||||||
case 'name':
|
|
||||||
return {'w.lastName': {like: `%${value}%`}};
|
|
||||||
case 'firstName':
|
case 'firstName':
|
||||||
return {'w.firstName': {like: `%${value}%`}};
|
return {'w.firstName': {like: `%${value}%`}};
|
||||||
|
case 'lastName':
|
||||||
|
return {'w.lastName': {like: `%${value}%`}};
|
||||||
case 'extension':
|
case 'extension':
|
||||||
return {'p.extension': value};
|
return {'p.extension': value};
|
||||||
case 'fi':
|
case 'fi':
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Worker allocatePDA()', () => {
|
||||||
|
it('should allocate a new worker\'s PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
try {
|
||||||
|
const workerWithoutPDA = 1101;
|
||||||
|
const PDAWithIdleState = 4;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithoutPDA, pda: PDAWithIdleState}};
|
||||||
|
|
||||||
|
await models.Worker.allocatePDA(ctx, options);
|
||||||
|
const deviceProduction = await models.DeviceProduction.findById(PDAWithIdleState, null, options);
|
||||||
|
const deviceProductionUser = await models.DeviceProductionUser
|
||||||
|
.findById(PDAWithIdleState, null, options);
|
||||||
|
|
||||||
|
expect(deviceProduction.stateFk).toEqual('active');
|
||||||
|
expect(deviceProductionUser).not.toBeNull();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error trying to allocate a non idle PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const workerWithoutPDA = 1101;
|
||||||
|
const PDAWithActiveState = 1;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithoutPDA, pda: PDAWithActiveState}};
|
||||||
|
|
||||||
|
await models.Worker.allocatePDA(ctx, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Worker deallocatePDA()', () => {
|
||||||
|
it('should deallocate a worker\'s PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
try {
|
||||||
|
const workerWithPDA = 1;
|
||||||
|
const PDAWithActiveState = 1;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithPDA, pda: PDAWithActiveState}};
|
||||||
|
|
||||||
|
await models.Worker.deallocatePDA(ctx, options);
|
||||||
|
const deviceProduction = await models.DeviceProduction.findById(PDAWithActiveState, null, options);
|
||||||
|
const deviceProductionUser = await models.DeviceProductionUser
|
||||||
|
.findById(PDAWithActiveState, null, options);
|
||||||
|
|
||||||
|
expect(deviceProduction.stateFk).toEqual('idle');
|
||||||
|
expect(deviceProductionUser).toBe(null);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -16,7 +16,7 @@ describe('worker filter()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 2 results filtering by name', async() => {
|
it('should return 2 results filtering by name', async() => {
|
||||||
let result = await app.models.Worker.filter({args: {filter: {}, name: 'agency'}});
|
let result = await app.models.Worker.filter({args: {filter: {}, firstName: 'agency'}});
|
||||||
|
|
||||||
expect(result.length).toEqual(2);
|
expect(result.length).toEqual(2);
|
||||||
expect(result[0].nickname).toEqual('agencyNick');
|
expect(result[0].nickname).toEqual('agencyNick');
|
||||||
|
|
|
@ -20,6 +20,18 @@
|
||||||
"Device": {
|
"Device": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"DeviceProduction": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionModels": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionState": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionUser": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"EducationLevel": {
|
"EducationLevel": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionModels",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionModels"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionState",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionState"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionUser",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionUser"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"deviceProductionFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"deviceProduction": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProduction",
|
||||||
|
"foreignKey": "deviceProductionFk"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "User",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProduction",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProduction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"imei": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"modelFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"macWifi": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"serialNumber": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"android_id": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"purchased": {
|
||||||
|
"type" : "date"
|
||||||
|
},
|
||||||
|
"stateFk": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"isInScaleFusion": {
|
||||||
|
"type" : "boolean"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"model": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProductionModels",
|
||||||
|
"foreignKey": "modelFk"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProductionState",
|
||||||
|
"foreignKey": "stateFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,10 @@ module.exports = Self => {
|
||||||
require('../methods/worker/holidays')(Self);
|
require('../methods/worker/holidays')(Self);
|
||||||
require('../methods/worker/activeContract')(Self);
|
require('../methods/worker/activeContract')(Self);
|
||||||
require('../methods/worker/new')(Self);
|
require('../methods/worker/new')(Self);
|
||||||
|
require('../methods/worker/deallocatePDA')(Self);
|
||||||
|
require('../methods/worker/allocatePDA')(Self);
|
||||||
|
|
||||||
|
Self.validatesUniquenessOf('locker', {
|
||||||
|
message: 'This locker has already been assigned'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"locker": {
|
||||||
|
"type" : "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -78,6 +78,11 @@
|
||||||
ng-model="$ctrl.worker.SSN"
|
ng-model="$ctrl.worker.SSN"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
<vn-input-number
|
||||||
|
min="0"
|
||||||
|
label="Locker"
|
||||||
|
ng-model="$ctrl.worker.locker">
|
||||||
|
</vn-input-number>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -6,3 +6,4 @@ Married: Casado/a
|
||||||
Single: Soltero/a
|
Single: Soltero/a
|
||||||
Business phone: Teléfono de empresa
|
Business phone: Teléfono de empresa
|
||||||
Mobile extension: Extensión móvil
|
Mobile extension: Extensión móvil
|
||||||
|
Locker: Taquilla
|
||||||
|
|
|
@ -10,6 +10,7 @@ import './descriptor-popover';
|
||||||
import './search-panel';
|
import './search-panel';
|
||||||
import './basic-data';
|
import './basic-data';
|
||||||
import './pbx';
|
import './pbx';
|
||||||
|
import './pda';
|
||||||
import './department';
|
import './department';
|
||||||
import './calendar';
|
import './calendar';
|
||||||
import './time-control';
|
import './time-control';
|
||||||
|
|
|
@ -24,3 +24,10 @@ Go to the worker: Ir al trabajador
|
||||||
Click to exclude the user from getting disabled: Marcar para no deshabilitar
|
Click to exclude the user from getting disabled: Marcar para no deshabilitar
|
||||||
Click to allow the user to be disabled: Marcar para deshabilitar
|
Click to allow the user to be disabled: Marcar para deshabilitar
|
||||||
This user can't be disabled: Fijado para no deshabilitar
|
This user can't be disabled: Fijado para no deshabilitar
|
||||||
|
Model: Modelo
|
||||||
|
Serial Number: Número de serie
|
||||||
|
Current PDA: PDA Actual
|
||||||
|
Deallocate PDA: Desasignar PDA
|
||||||
|
PDA deallocated: PDA desasignada
|
||||||
|
PDA allocated: PDA asignada
|
||||||
|
New PDA: Nueva PDA
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<div class="vn-w-md" ng-show="$ctrl.currentPDA">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
label="Current PDA"
|
||||||
|
ng-model="$ctrl.currentPDA.description"
|
||||||
|
disabled="true">
|
||||||
|
<append>
|
||||||
|
<vn-icon-button
|
||||||
|
icon="delete"
|
||||||
|
vn-tooltip="Deallocate PDA"
|
||||||
|
ng-click="$ctrl.deallocatePDA()"
|
||||||
|
vn-acl="hr, productionAssi">
|
||||||
|
</vn-icon-button>
|
||||||
|
</append>
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
</div>
|
||||||
|
<form name="form" ng-show="!$ctrl.currentPDA" ng-submit="$ctrl.allocatePDA()" class="vn-w-md">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-acl="hr, productionAssi"
|
||||||
|
ng-model="$ctrl.newPDA"
|
||||||
|
url="DeviceProductions"
|
||||||
|
fields="['id', 'modelFk', 'serialNumber']"
|
||||||
|
where="{'stateFk': 'idle'}"
|
||||||
|
label="New PDA"
|
||||||
|
order="id"
|
||||||
|
value-field="id"
|
||||||
|
show-field="serialNumber">
|
||||||
|
<tpl-item>
|
||||||
|
<span>ID: {{id}}</span>
|
||||||
|
<span class="separator"></span>
|
||||||
|
<span>{{'Model' | translate}}: {{modelFk}}</span>
|
||||||
|
<span class="separator"></span>
|
||||||
|
<span>{{'Serial Number' | translate}}: {{serialNumber}}</span>
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
disabled="!$ctrl.newPDA"
|
||||||
|
label="Assign">
|
||||||
|
</vn-submit>
|
||||||
|
</vn-button-bar>
|
||||||
|
</form>
|
|
@ -0,0 +1,53 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
const filter = {
|
||||||
|
where: {userFk: this.$params.id},
|
||||||
|
include: {relation: 'deviceProduction'}
|
||||||
|
};
|
||||||
|
this.$http.get('DeviceProductionUsers', {filter}).
|
||||||
|
then(res => {
|
||||||
|
if (res.data && res.data.length > 0)
|
||||||
|
this.setCurrentPDA(res.data[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deallocatePDA() {
|
||||||
|
this.$http.post(`Workers/${this.$params.id}/deallocatePDA`, {pda: this.currentPDA.deviceProductionFk})
|
||||||
|
.then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('PDA deallocated'));
|
||||||
|
delete this.currentPDA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatePDA() {
|
||||||
|
this.$http.post(`Workers/${this.$params.id}/allocatePDA`, {pda: this.newPDA})
|
||||||
|
.then(res => {
|
||||||
|
if (res.data)
|
||||||
|
this.setCurrentPDA(res.data);
|
||||||
|
|
||||||
|
this.vnApp.showSuccess(this.$t('PDA allocated'));
|
||||||
|
delete this.newPDA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPDA(data) {
|
||||||
|
this.currentPDA = data;
|
||||||
|
this.currentPDA.description = [];
|
||||||
|
this.currentPDA.description.push(`ID: ${this.currentPDA.deviceProductionFk}`);
|
||||||
|
this.currentPDA.description.push(`${this.$t('Model')}: ${this.currentPDA.deviceProduction.modelFk}`);
|
||||||
|
this.currentPDA.description.push(`${this.$t('Serial Number')}: ${this.currentPDA.deviceProduction.serialNumber}`);
|
||||||
|
this.currentPDA.description = this.currentPDA.description.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnWorkerPda', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
});
|
|
@ -0,0 +1,72 @@
|
||||||
|
import './index';
|
||||||
|
|
||||||
|
describe('Worker', () => {
|
||||||
|
describe('Component vnWorkerPda', () => {
|
||||||
|
let $httpBackend;
|
||||||
|
let $scope;
|
||||||
|
let $element;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
beforeEach(ngModule('worker'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
$element = angular.element('<vn-worker-pda></vn-worker-pda>');
|
||||||
|
controller = $componentController('vnWorkerPda', {$element, $scope});
|
||||||
|
$httpBackend.expectGET(`DeviceProductionUsers`).respond();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('deallocatePDA()', () => {
|
||||||
|
it('should make an HTTP Post query to deallocatePDA', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
controller.currentPDA = {deviceProductionFk: 1};
|
||||||
|
controller.$params.id = 1;
|
||||||
|
|
||||||
|
$httpBackend
|
||||||
|
.expectPOST(`Workers/${controller.$params.id}/deallocatePDA`,
|
||||||
|
{pda: controller.currentPDA.deviceProductionFk})
|
||||||
|
.respond();
|
||||||
|
controller.deallocatePDA();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
expect(controller.currentPDA).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('allocatePDA()', () => {
|
||||||
|
it('should make an HTTP Post query to allocatePDA', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
controller.newPDA = 4;
|
||||||
|
controller.$params.id = 1;
|
||||||
|
|
||||||
|
$httpBackend
|
||||||
|
.expectPOST(`Workers/${controller.$params.id}/allocatePDA`,
|
||||||
|
{pda: controller.newPDA})
|
||||||
|
.respond();
|
||||||
|
controller.allocatePDA();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
expect(controller.newPDA).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCurrentPDA()', () => {
|
||||||
|
it('should set CurrentPDA', () => {
|
||||||
|
const data = {
|
||||||
|
deviceProductionFk: 1,
|
||||||
|
deviceProduction: {
|
||||||
|
modelFk: 1,
|
||||||
|
serialNumber: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controller.setCurrentPDA(data);
|
||||||
|
|
||||||
|
expect(controller.currentPDA).toBeDefined();
|
||||||
|
expect(controller.currentPDA.description).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
span.separator{
|
||||||
|
border-left: 1px solid black;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "worker.card.basicData", "icon": "settings"},
|
{"state": "worker.card.basicData", "icon": "settings"},
|
||||||
{"state": "worker.card.pbx", "icon": "icon-pbx"},
|
|
||||||
{"state": "worker.card.calendar", "icon": "icon-calendar"},
|
|
||||||
{"state": "worker.card.timeControl", "icon": "access_time"},
|
{"state": "worker.card.timeControl", "icon": "access_time"},
|
||||||
|
{"state": "worker.card.calendar", "icon": "icon-calendar"},
|
||||||
|
{"state": "worker.card.pda", "icon": "phone_android"},
|
||||||
|
{"state": "worker.card.pbx", "icon": "icon-pbx"},
|
||||||
{"state": "worker.card.dms.index", "icon": "cloud_upload"},
|
{"state": "worker.card.dms.index", "icon": "cloud_upload"},
|
||||||
{
|
{
|
||||||
"icon": "icon-wiki",
|
"icon": "icon-wiki",
|
||||||
|
@ -141,6 +142,13 @@
|
||||||
"component": "vn-worker-create",
|
"component": "vn-worker-create",
|
||||||
"description": "New worker",
|
"description": "New worker",
|
||||||
"acl": ["hr"]
|
"acl": ["hr"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/pda",
|
||||||
|
"state": "worker.card.pda",
|
||||||
|
"component": "vn-worker-pda",
|
||||||
|
"description": "PDA",
|
||||||
|
"acl": ["hr", "productionAssi"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Last name"
|
label="Last name"
|
||||||
ng-model="filter.name">
|
ng-model="filter.lastName">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
<vn-label-value label="Personal phone"
|
<vn-label-value label="Personal phone"
|
||||||
value="{{worker.client.phone}}">
|
value="{{worker.client.phone}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Locker"
|
||||||
|
value="{{worker.locker}}">
|
||||||
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 translate>User data</h4>
|
<h4 translate>User data</h4>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Business phone: Teléfono de empresa
|
Business phone: Teléfono de empresa
|
||||||
Personal phone: Teléfono personal
|
Personal phone: Teléfono personal
|
||||||
Mobile extension: Extensión móvil
|
Mobile extension: Extensión móvil
|
||||||
|
Locker: Taquilla
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.08.01",
|
"version": "23.10.01",
|
||||||
"author": "Verdnatura Levante SL",
|
"author": "Verdnatura Levante SL",
|
||||||
"description": "Salix backend",
|
"description": "Salix backend",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
|
|
@ -66,8 +66,10 @@ module.exports = {
|
||||||
else
|
else
|
||||||
value = '---';
|
value = '---';
|
||||||
|
|
||||||
if (labelData.routeFk)
|
if (labelData.routeFk) {
|
||||||
value = `${value} [${labelData.routeFk.toString().substring(0, 3)}]`;
|
let routeFk = labelData.routeFk.toString();
|
||||||
|
value = `${value} [${routeFk.substring(routeFk.length - 3)}]`;
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
"height": "4.9cm",
|
"height": "4.9cm",
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": "0.3cm",
|
"top": "0.3cm",
|
||||||
"right": "0.6cm",
|
"right": "0.3cm",
|
||||||
"bottom": "0cm",
|
"bottom": "0cm",
|
||||||
"left": "0cm"
|
"left": "0.2cm"
|
||||||
},
|
},
|
||||||
"printBackground": true
|
"printBackground": true
|
||||||
}
|
}
|
Loading…
Reference in New Issue