diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17259f545..cf7d8465a 100644
--- a/CHANGELOG.md
+++ b/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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2310.01] - 2023-03-23
+
+### Added
+-
+
+### Changed
+-
+
+### 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
--
-
-### Fixed
--
+- (Ticket -> Borrar ticket) Restringido el borrado de tickets con abono
## [2306.01] - 2023-02-23
diff --git a/back/tests.js b/back/tests.js
index ab6893791..8f7cfd3d4 100644
--- a/back/tests.js
+++ b/back/tests.js
@@ -30,7 +30,10 @@ async function test() {
const bootOptions = {dataSources};
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 = new Jasmine();
diff --git a/db/changes/230601/00-acl_claim.sql b/db/changes/230601/00-acl_claim.sql
new file mode 100644
index 000000000..4e680eb4f
--- /dev/null
+++ b/db/changes/230601/00-acl_claim.sql
@@ -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';
+
diff --git a/db/changes/230801/00-supplierIsVies.sql b/db/changes/230801/00-supplierIsVies.sql
new file mode 100644
index 000000000..5861e7615
--- /dev/null
+++ b/db/changes/230801/00-supplierIsVies.sql
@@ -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);
diff --git a/db/changes/230801/00-workerLocker.sql b/db/changes/230801/00-workerLocker.sql
new file mode 100644
index 000000000..0a72cca1e
--- /dev/null
+++ b/db/changes/230801/00-workerLocker.sql
@@ -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');
diff --git a/db/changes/230801/01-sage_supplierAdd.sql b/db/changes/230801/01-sage_supplierAdd.sql
new file mode 100644
index 000000000..66cb0aff1
--- /dev/null
+++ b/db/changes/230801/01-sage_supplierAdd.sql
@@ -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 ;
diff --git a/db/changes/230801/.gitkeep b/db/changes/231001/.gitkeep
similarity index 100%
rename from db/changes/230801/.gitkeep
rename to db/changes/231001/.gitkeep
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 8fb12e822..80983a318 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1759,12 +1759,12 @@ INSERT INTO `vn`.`clientSample`(`id`, `clientFk`, `typeFk`, `created`, `workerFk
INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`, `hasToNotify`)
VALUES
( 1, 'pending', 'Pendiente', 1, 1, 0),
- ( 2, 'managed', 'Gestionado', 1, 5, 0),
+ ( 2, 'managed', 'Gestionado', 72, 5, 0),
( 3, 'resolved', 'Resuelto', 72, 7, 0),
( 4, 'canceled', 'Anulado', 72, 6, 1),
- ( 5, 'incomplete', 'Incompleta', 72, 3, 1),
- ( 6, 'mana', 'Mana', 1, 4, 0),
- ( 7, 'lack', 'Faltas', 1, 2, 0);
+ ( 5, 'incomplete', 'Incompleta', 1, 3, 1),
+ ( 6, 'mana', 'Mana', 72, 4, 0),
+ ( 7, 'lack', 'Faltas', 72, 2, 0);
INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`, `rma`)
VALUES
@@ -1828,7 +1828,12 @@ INSERT INTO vn.claimRma (`id`, `code`, `created`, `workerFk`)
(4, '02676A049183', DEFAULT, 1107),
(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`)
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', 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`)
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');
@@ -2787,3 +2791,34 @@ INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`)
VALUES
(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());
+
+
diff --git a/db/tests/vn/ticketCalculateClon.spec.js b/db/tests/vn/ticketCalculateClon.spec.js
index a3c790492..9116d805f 100644
--- a/db/tests/vn/ticketCalculateClon.spec.js
+++ b/db/tests/vn/ticketCalculateClon.spec.js
@@ -22,7 +22,7 @@ describe('ticket ticketCalculateClon()', () => {
originalTicketId: 11
};
- stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
+ stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -31,7 +31,8 @@ describe('ticket ticketCalculateClon()', () => {
params.agencyType,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
@@ -71,7 +72,7 @@ describe('ticket ticketCalculateClon()', () => {
originalTicketId: 11
};
- stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
+ stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result)', [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -80,7 +81,8 @@ describe('ticket ticketCalculateClon()', () => {
params.agencyType,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
diff --git a/db/tests/vn/ticketCreateWithUser.spec.js b/db/tests/vn/ticketCreateWithUser.spec.js
index 4aeece564..5dd84d397 100644
--- a/db/tests/vn/ticketCreateWithUser.spec.js
+++ b/db/tests/vn/ticketCreateWithUser.spec.js
@@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
-describe('ticket ticketCreateWithUser()', () => {
+describe('ticket ticket_add()', () => {
const today = Date.vnNew();
it('should confirm the procedure creates the expected ticket', async() => {
let stmts = [];
@@ -21,7 +21,7 @@ describe('ticket ticketCreateWithUser()', () => {
userId: 18
};
- stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
+ stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -30,7 +30,8 @@ describe('ticket ticketCreateWithUser()', () => {
params.agencyModeFk,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
@@ -70,7 +71,7 @@ describe('ticket ticketCreateWithUser()', () => {
userId: 18
};
- stmt = new ParameterizedSQL('CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)', [
+ stmt = new ParameterizedSQL('CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)', [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -79,7 +80,8 @@ describe('ticket ticketCreateWithUser()', () => {
params.agencyModeFk,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
@@ -120,7 +122,7 @@ describe('ticket ticketCreateWithUser()', () => {
userId: 18
};
- stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
+ stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -129,7 +131,8 @@ describe('ticket ticketCreateWithUser()', () => {
params.agencyModeFk,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
@@ -172,7 +175,7 @@ describe('ticket ticketCreateWithUser()', () => {
]);
stmts.push(stmt);
- stmt = new ParameterizedSQL(`CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
+ stmt = new ParameterizedSQL(`CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @newTicketId)`, [
params.clientFk,
params.shipped,
params.warehouseFk,
@@ -181,7 +184,8 @@ describe('ticket ticketCreateWithUser()', () => {
params.agencyModeFk,
params.routeFk,
params.landed,
- params.userId
+ params.userId,
+ true
]);
stmts.push(stmt);
stmts.push(`select @newTicketId`);
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index da24d4f95..a783d0a11 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -22,6 +22,7 @@ export default {
userConfigSecondAutocomplete: '#localBank',
userConfigThirdAutocomplete: '#localCompany',
acceptButton: '.vn-confirm.shown button[response=accept]',
+ cancelButton: '.vn-confirm.shown input[response=cancel]',
searchButton: 'vn-searchbar vn-icon[icon="search"]'
},
moduleIndex: {
@@ -973,6 +974,7 @@ export default {
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',
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',
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',
@@ -983,6 +985,7 @@ export default {
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
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]'
},
workerPbx: {
@@ -1040,6 +1043,12 @@ export default {
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
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: {
topbarSearch: 'vn-searchbar',
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
diff --git a/e2e/paths/03-worker/01_summary.spec.js b/e2e/paths/03-worker/01_summary.spec.js
index 4e5b0cfa9..51992b41d 100644
--- a/e2e/paths/03-worker/01_summary.spec.js
+++ b/e2e/paths/03-worker/01_summary.spec.js
@@ -29,5 +29,6 @@ describe('Worker summary path', () => {
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.extension, 'innerText')).toEqual('1101');
+ expect(await page.getProperty(selectors.workerSummary.locker, 'innerText')).toEqual('-');
});
});
diff --git a/e2e/paths/03-worker/02_basicData.spec.js b/e2e/paths/03-worker/02_basicData.spec.js
index 66a597dd1..381375dc7 100644
--- a/e2e/paths/03-worker/02_basicData.spec.js
+++ b/e2e/paths/03-worker/02_basicData.spec.js
@@ -25,6 +25,7 @@ describe('Worker basic data path', () => {
await page.overwrite(selectors.workerBasicData.name, 'David C.');
await page.overwrite(selectors.workerBasicData.surname, 'H.');
await page.overwrite(selectors.workerBasicData.phone, '444332211');
+ await page.overwrite(selectors.workerBasicData.locker, '1');
await page.click(selectors.workerBasicData.saveButton);
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.surname, 'value')).toEqual('H.');
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
+ expect(await page.waitToGetProperty(selectors.workerBasicData.locker, 'value')).toEqual('1');
});
});
diff --git a/e2e/paths/03-worker/07_pda.spec.js b/e2e/paths/03-worker/07_pda.spec.js
new file mode 100644
index 000000000..2b743823e
--- /dev/null
+++ b/e2e/paths/03-worker/07_pda.spec.js
@@ -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');
+ });
+});
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index 9e4898be9..36161ae1d 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -227,9 +227,21 @@ describe('Ticket Edit sale path', () => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
+ await page.waitForSnackbar();
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() => {
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
diff --git a/e2e/paths/05-ticket/21_future.spec.js b/e2e/paths/05-ticket/21_future.spec.js
index 34ae3d688..2b8057247 100644
--- a/e2e/paths/05-ticket/21_future.spec.js
+++ b/e2e/paths/05-ticket/21_future.spec.js
@@ -4,12 +4,17 @@ import getBrowser from '../../helpers/puppeteer';
describe('Ticket Future path', () => {
let browser;
let page;
+ let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSection('ticket.future');
+ page.on('request', req => {
+ if (req.url().includes(`Tickets/getTicketsFuture`))
+ httpRequest = req.url();
+ });
});
afterAll(async() => {
@@ -42,114 +47,82 @@ describe('Ticket Future path', () => {
it('should search with the required data', async() => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
- await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
+
+ expect(httpRequest).toBeDefined();
});
it('should search with the origin IPT', async() => {
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.waitToClick(selectors.ticketFuture.submit);
- await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
+
+ expect(httpRequest).toContain('ipt=H');
});
it('should search with the destination IPT', async() => {
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.futureIpt, 'Horizontal');
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() => {
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.state, 'Free');
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() => {
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.futureState, 'Free');
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.clearInput(selectors.ticketFuture.ipt);
- await page.clearInput(selectors.ticketFuture.futureIpt);
- await page.clearInput(selectors.ticketFuture.state);
await page.clearInput(selectors.ticketFuture.futureState);
-
await page.waitToClick(selectors.ticketFuture.submit);
- await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search in smart-table with an ID Origin', async() => {
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.waitForNumberOfElements(selectors.ticketFuture.table, 2);
+ expect(httpRequest).toContain('id');
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() => {
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
- await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Vertical');
- await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
+ await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Horizontal');
+ 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.openAdvancedSearchButton);
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() => {
diff --git a/e2e/paths/05-ticket/22_advance.spec.js b/e2e/paths/05-ticket/22_advance.spec.js
index c92cd7a20..f8442bf12 100644
--- a/e2e/paths/05-ticket/22_advance.spec.js
+++ b/e2e/paths/05-ticket/22_advance.spec.js
@@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Ticket Advance path', () => {
let browser;
let page;
- const httpRequests = [];
+ let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
@@ -13,7 +13,7 @@ describe('Ticket Advance path', () => {
await page.accessToSection('ticket.advance');
page.on('request', req => {
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.submit);
- expect(httpRequests.length).toBeGreaterThan(0);
+ expect(httpRequest).toBeDefined();
});
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.waitToClick(selectors.ticketAdvance.submit);
- const request = httpRequests.find(req => req.includes(('futureIpt=H')));
-
- expect(request).toBeDefined();
-
- httpRequests.splice(httpRequests.indexOf(request), 1);
+ expect(httpRequest).toContain('futureIpt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureIpt);
@@ -73,11 +69,7 @@ describe('Ticket Advance path', () => {
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
await page.waitToClick(selectors.ticketAdvance.submit);
- const request = httpRequests.find(req => req.includes(('ipt=H')));
-
- expect(request).toBeDefined();
-
- httpRequests.splice(httpRequests.indexOf(request), 1);
+ expect(httpRequest).toContain('ipt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.ipt);
@@ -88,11 +80,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical');
- const request = httpRequests.find(req => req.includes(('futureIpt')));
-
- expect(request).toBeDefined();
-
- httpRequests.splice(httpRequests.indexOf(request), 1);
+ expect(httpRequest).toContain('futureIpt');
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
@@ -103,11 +91,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical');
- const request = httpRequests.find(req => req.includes(('ipt')));
-
- expect(request).toBeDefined();
-
- httpRequests.splice(httpRequests.indexOf(request), 1);
+ expect(httpRequest).toContain('ipt');
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
diff --git a/front/core/components/autocomplete/style.scss b/front/core/components/autocomplete/style.scss
index 19e8362d5..201d29c1e 100755
--- a/front/core/components/autocomplete/style.scss
+++ b/front/core/components/autocomplete/style.scss
@@ -19,6 +19,10 @@
padding-right: 0;
}
}
+
+ & > .icons.pre {
+ min-width: 22px
+ }
}
&.readonly > .container > .icons.post {
display: none;
diff --git a/front/core/components/check/index.js b/front/core/components/check/index.js
index 78b1807a5..25ec506ad 100644
--- a/front/core/components/check/index.js
+++ b/front/core/components/check/index.js
@@ -40,7 +40,7 @@ export default class Check extends Toggle {
set tripleState(value) {
this._tripleState = value;
- this.field = this.field;
+ this.field = value;
}
get tripleState() {
diff --git a/front/core/components/check/index.spec.js b/front/core/components/check/index.spec.js
index c9d50cab2..7ec0f12a0 100644
--- a/front/core/components/check/index.spec.js
+++ b/front/core/components/check/index.spec.js
@@ -45,8 +45,8 @@ describe('Component vnCheck', () => {
});
it(`should set value to null and change to true when clicked`, () => {
- controller.field = null;
controller.tripleState = true;
+ controller.field = null;
element.click();
expect(controller.field).toEqual(true);
diff --git a/front/core/components/chip/style.scss b/front/core/components/chip/style.scss
index 7651638be..562861441 100644
--- a/front/core/components/chip/style.scss
+++ b/front/core/components/chip/style.scss
@@ -58,10 +58,33 @@ vn-chip {
background-color: $color-font-bg-dark;
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 {
@extend %clickable;
opacity: 0.8;
-
+
&:hover,
&:focus {
opacity: 1;
@@ -118,4 +141,4 @@ vn-avatar {
display: inline-block;
min-width: 28px;
border-radius: 50%;
-}
\ No newline at end of file
+}
diff --git a/front/core/components/field/index.html b/front/core/components/field/index.html
index bd13f46d6..0d65c1f40 100644
--- a/front/core/components/field/index.html
+++ b/front/core/components/field/index.html
@@ -12,7 +12,7 @@
*
-
+
.icons.pre.clearable {
+ min-width: 22px
+ }
& > .underline {
position: absolute;
bottom: 0;
diff --git a/front/core/components/smart-table/index.js b/front/core/components/smart-table/index.js
index ad9883950..b2380a62f 100644
--- a/front/core/components/smart-table/index.js
+++ b/front/core/components/smart-table/index.js
@@ -436,6 +436,7 @@ export default class SmartTable extends Component {
if (filters && filters.userFilter)
this.model.userFilter = filters.userFilter;
+
this.addFilter(field, this.$inputsScope.searchProps[field]);
}
@@ -451,7 +452,7 @@ export default class SmartTable extends Component {
}
addFilter(field, value) {
- if (value == '') value = null;
+ if (value === '') value = null;
let stateFilter = {tableQ: {}};
if (this.$params.q) {
@@ -462,7 +463,7 @@ export default class SmartTable extends Component {
}
const whereParams = {[field]: value};
- if (value) {
+ if (value !== '' && value !== null && value !== undefined) {
let where = {[field]: value};
if (this.exprBuilder) {
where = buildFilter(whereParams, (param, value) =>
diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml
index d92c32b33..bed41c63b 100644
--- a/front/salix/locale/es.yml
+++ b/front/salix/locale/es.yml
@@ -23,6 +23,7 @@ There is a new version, click here to reload: Hay una nueva versión, pulse aqu
Back: Volver
Save: Guardar
+Assign: Asignar
Create: Crear
Send: Enviar
Delete: Eliminar
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index c810e5a69..dbe25dea3 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -147,8 +147,10 @@
"Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated",
"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 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",
- "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"
+}
\ No newline at end of file
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index ad6d53d64..507cc9003 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -259,10 +259,12 @@
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload file": "Error al subir archivo",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
- "It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
- "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 tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
+ "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",
"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"
}
-
diff --git a/modules/claim/back/methods/claim/isEditable.js b/modules/claim/back/methods/claim-state/isEditable.js
similarity index 52%
rename from modules/claim/back/methods/claim/isEditable.js
rename to modules/claim/back/methods/claim-state/isEditable.js
index cd14d70c7..2d0a8dc44 100644
--- a/modules/claim/back/methods/claim/isEditable.js
+++ b/modules/claim/back/methods/claim-state/isEditable.js
@@ -1,12 +1,12 @@
module.exports = Self => {
Self.remoteMethodCtx('isEditable', {
- description: 'Check if a claim is editable',
+ description: 'Check if an state is editable',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
- description: 'the claim id',
+ description: 'the state id',
http: {source: 'path'}
}],
returns: {
@@ -21,25 +21,18 @@ module.exports = Self => {
Self.isEditable = async(ctx, id, options) => {
const userId = ctx.req.accessToken.userId;
+ const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
-
- const isClaimManager = await Self.app.models.Account.hasRole(userId, 'claimManager', myOptions);
-
- const claim = await Self.app.models.Claim.findById(id, {
- fields: ['claimStateFk'],
- include: [{
- relation: 'claimState'
- }]
- }, myOptions);
-
- const isClaimResolved = claim && claim.claimState().code == 'resolved';
-
- if (!claim || (isClaimResolved && !isClaimManager))
- return false;
-
- return true;
+
+ const state = await models.ClaimState.findById(id, {
+ include: {
+ relation: 'writeRole'
+ }
+ }, myOptions);
+ const roleWithGrants = state && state.writeRole().name;
+ return await models.Account.hasRole(userId, roleWithGrants, myOptions);
};
};
diff --git a/modules/claim/back/methods/claim/specs/isEditable.spec.js b/modules/claim/back/methods/claim-state/specs/isEditable.spec.js
similarity index 74%
rename from modules/claim/back/methods/claim/specs/isEditable.spec.js
rename to modules/claim/back/methods/claim-state/specs/isEditable.spec.js
index 3afea7843..1fb8e1536 100644
--- a/modules/claim/back/methods/claim/specs/isEditable.spec.js
+++ b/modules/claim/back/methods/claim-state/specs/isEditable.spec.js
@@ -1,16 +1,16 @@
const app = require('vn-loopback/server/server');
-describe('claim isEditable()', () => {
- const salesPerdonId = 18;
+describe('claimstate isEditable()', () => {
+ const salesPersonId = 18;
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({});
try {
const options = {transaction: tx};
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);
@@ -27,8 +27,8 @@ describe('claim isEditable()', () => {
try {
const options = {transaction: tx};
- const ctx = {req: {accessToken: {userId: salesPerdonId}}};
- const result = await app.models.Claim.isEditable(ctx, 4, options);
+ const ctx = {req: {accessToken: {userId: salesPersonId}}};
+ const result = await app.models.ClaimState.isEditable(ctx, 3, options);
expect(result).toEqual(false);
@@ -46,7 +46,7 @@ describe('claim isEditable()', () => {
const options = {transaction: tx};
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);
@@ -63,8 +63,8 @@ describe('claim isEditable()', () => {
try {
const options = {transaction: tx};
- const ctx = {req: {accessToken: {userId: salesPerdonId}}};
- const result = await app.models.Claim.isEditable(ctx, 1, options);
+ const ctx = {req: {accessToken: {userId: claimManagerId}}};
+ const result = await app.models.ClaimState.isEditable(ctx, 7, options);
expect(result).toEqual(true);
diff --git a/modules/claim/back/methods/claim/getSummary.js b/modules/claim/back/methods/claim/getSummary.js
index ca376f853..d384f7ebb 100644
--- a/modules/claim/back/methods/claim/getSummary.js
+++ b/modules/claim/back/methods/claim/getSummary.js
@@ -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
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
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
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
filter = {
@@ -145,11 +146,11 @@ module.exports = Self => {
{relation: 'claimBeggining'}
]
};
- promises.push(Self.app.models.ClaimEnd.find(filter, myOptions));
+ promises.push(models.ClaimEnd.find(filter, myOptions));
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.salesClaimed = res[1];
summary.observations = res[2];
diff --git a/modules/claim/back/methods/claim/logs.js b/modules/claim/back/methods/claim/logs.js
new file mode 100644
index 000000000..c7e69680b
--- /dev/null
+++ b/modules/claim/back/methods/claim/logs.js
@@ -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;
+ };
+};
diff --git a/modules/claim/back/methods/claim/specs/log.spec.js b/modules/claim/back/methods/claim/specs/log.spec.js
new file mode 100644
index 000000000..0ae534f1e
--- /dev/null
+++ b/modules/claim/back/methods/claim/specs/log.spec.js
@@ -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));
+ });
+});
diff --git a/modules/claim/back/methods/claim/updateClaim.js b/modules/claim/back/methods/claim/updateClaim.js
index cc9937c19..5271136d6 100644
--- a/modules/claim/back/methods/claim/updateClaim.js
+++ b/modules/claim/back/methods/claim/updateClaim.js
@@ -2,6 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('updateClaim', {
description: 'Update a claim with privileges',
+ accessType: 'WRITE',
accepts: [{
arg: 'ctx',
type: 'object',
@@ -78,11 +79,11 @@ module.exports = Self => {
// Validate when claimState has been changed
if (args.claimStateFk) {
- const canUpdate = await canChangeState(ctx, claim.claimStateFk, myOptions);
- const hasRights = await canChangeState(ctx, args.claimStateFk, myOptions);
+ const canEditOldState = await models.ClaimState.isEditable(ctx, claim.claimStateFk, myOptions);
+ const canEditNewState = await models.ClaimState.isEditable(ctx, args.claimStateFk, 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`);
}
@@ -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) {
const models = Self.app.models;
const origin = ctx.req.headers.origin;
diff --git a/modules/claim/back/models/claim-beginning.js b/modules/claim/back/models/claim-beginning.js
index 681aaebc7..ba70c0bbc 100644
--- a/modules/claim/back/models/claim-beginning.js
+++ b/modules/claim/back/models/claim-beginning.js
@@ -22,8 +22,28 @@ module.exports = Self => {
async function claimIsEditable(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
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 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)
throw new UserError(`The current claim can't be modified`);
diff --git a/modules/claim/back/models/claim-state.js b/modules/claim/back/models/claim-state.js
new file mode 100644
index 000000000..e0df5ac4d
--- /dev/null
+++ b/modules/claim/back/models/claim-state.js
@@ -0,0 +1,3 @@
+module.exports = Self => {
+ require('../methods/claim-state/isEditable')(Self);
+};
diff --git a/modules/claim/back/models/claim.js b/modules/claim/back/models/claim.js
index c9d7ee7d4..8e652f9fb 100644
--- a/modules/claim/back/models/claim.js
+++ b/modules/claim/back/models/claim.js
@@ -6,9 +6,9 @@ module.exports = Self => {
require('../methods/claim/regularizeClaim')(Self);
require('../methods/claim/uploadFile')(Self);
require('../methods/claim/updateClaimAction')(Self);
- require('../methods/claim/isEditable')(Self);
require('../methods/claim/updateClaimDestination')(Self);
require('../methods/claim/downloadFile')(Self);
require('../methods/claim/claimPickupPdf')(Self);
require('../methods/claim/claimPickupEmail')(Self);
+ require('../methods/claim/logs')(Self);
};
diff --git a/modules/claim/front/detail/index.js b/modules/claim/front/detail/index.js
index 7c3c04f44..833519579 100644
--- a/modules/claim/front/detail/index.js
+++ b/modules/claim/front/detail/index.js
@@ -100,8 +100,8 @@ class Controller extends Section {
}
setClaimedQuantity(id, claimedQuantity) {
- let params = {id: id, quantity: claimedQuantity};
- let query = `ClaimBeginnings/`;
+ let params = {quantity: claimedQuantity};
+ let query = `ClaimBeginnings/${id}`;
this.$http.patch(query, params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.calculateTotals();
@@ -151,7 +151,7 @@ class Controller extends Section {
isClaimEditable() {
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;
});
}
diff --git a/modules/claim/front/detail/index.spec.js b/modules/claim/front/detail/index.spec.js
index b36f3a172..a2b177281 100644
--- a/modules/claim/front/detail/index.spec.js
+++ b/modules/claim/front/detail/index.spec.js
@@ -17,7 +17,7 @@ describe('claim', () => {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('Claims/ClaimBeginnings').respond({});
$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('');
controller = $componentController('vnClaimDetail', {$element, $scope});
controller.claim = {
diff --git a/modules/invoiceOut/back/methods/invoiceOut/createPdf.js b/modules/invoiceOut/back/methods/invoiceOut/createPdf.js
index e56516237..71e7c1543 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/createPdf.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/createPdf.js
@@ -44,7 +44,7 @@ module.exports = Self => {
try {
const invoiceOut = await Self.findById(id, null, myOptions);
const hasInvoicing = await models.Account.hasRole(userId, 'invoicing', myOptions);
-
+ console.log(invoiceOut, !hasInvoicing);
if (invoiceOut.hasPdf && !hasInvoicing)
throw new UserError(`You don't have enough privileges`);
diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js
index fe005f1ab..208d55358 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js
@@ -47,6 +47,7 @@ module.exports = Self => {
ids = ids.split(',');
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');
const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions);
const fileName = extractFileName(invoiceOutPdf[2]);
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
index 26eae45ac..803338ef3 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
@@ -11,7 +11,6 @@ describe('InvoiceOut createPdf()', () => {
const ctx = {req: activeCtx};
it('should create a new PDF file and set true the hasPdf property', async() => {
- pending('https://redmine.verdnatura.es/issues/5035');
const invoiceId = 1;
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js
index 0f62a6876..41ea45487 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js
@@ -30,8 +30,6 @@ describe('InvoiceOut downloadZip()', () => {
});
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({});
let error;
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js
index d3d789393..9264bf77d 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js
@@ -65,8 +65,9 @@ describe('InvoiceOut filter()', () => {
await invoiceOut.updateAttribute('hasPdf', true, 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();
} catch (e) {
diff --git a/modules/item/back/methods/item/new.js b/modules/item/back/methods/item/new.js
index 0057cb50f..d066f2c9f 100644
--- a/modules/item/back/methods/item/new.js
+++ b/modules/item/back/methods/item/new.js
@@ -57,6 +57,7 @@ module.exports = Self => {
const itemType = await models.ItemType.findById(params.typeFk, {fields: ['isLaid']}, myOptions);
params.isLaid = itemType.isLaid;
+ params.isPhotoRequested = true;
const item = await models.Item.create(params, myOptions);
diff --git a/modules/item/back/models/item.json b/modules/item/back/models/item.json
index 2f58c30a9..10300f7a1 100644
--- a/modules/item/back/models/item.json
+++ b/modules/item/back/models/item.json
@@ -146,6 +146,12 @@
},
"isLaid": {
"type": "boolean"
+ },
+ "isPhotoRequested": {
+ "type": "boolean",
+ "mysql":{
+ "columnName": "doPhoto"
+ }
}
},
"relations": {
diff --git a/modules/item/front/basic-data/index.html b/modules/item/front/basic-data/index.html
index fd21ab240..974aa37d8 100644
--- a/modules/item/front/basic-data/index.html
+++ b/modules/item/front/basic-data/index.html
@@ -182,6 +182,12 @@
ng-model="$ctrl.item.isFragile"
info="Is shown at website, app that this item cannot travel (wreath, palms, ...)">
+
+
diff --git a/modules/item/front/basic-data/locale/es.yml b/modules/item/front/basic-data/locale/es.yml
index d59752ebb..747dfe792 100644
--- a/modules/item/front/basic-data/locale/es.yml
+++ b/modules/item/front/basic-data/locale/es.yml
@@ -11,4 +11,6 @@ Identifier: Identificador
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, ...)
Multiplier: Multiplicador
-Generic: Genérico
\ No newline at end of file
+Generic: Genérico
+This item does need a photo: Este artículo necesita una foto
+Do photo: Hacer foto
\ No newline at end of file
diff --git a/modules/item/front/descriptor/index.js b/modules/item/front/descriptor/index.js
index 972c89ae0..5f38d08dd 100644
--- a/modules/item/front/descriptor/index.js
+++ b/modules/item/front/descriptor/index.js
@@ -91,6 +91,9 @@ class Controller extends Descriptor {
this.$.photo.setAttribute('src', newSrc);
this.$.photo.setAttribute('zoom-image', newZoomSrc);
+
+ if (this.item.isPhotoRequested)
+ this.$http.patch(`Items/${this.item.id}`, {isPhotoRequested: false});
}
getWarehouseName(warehouseFk) {
diff --git a/modules/item/front/descriptor/index.spec.js b/modules/item/front/descriptor/index.spec.js
index 23053432e..dc2a5acf7 100644
--- a/modules/item/front/descriptor/index.spec.js
+++ b/modules/item/front/descriptor/index.spec.js
@@ -8,7 +8,8 @@ describe('vnItemDescriptor', () => {
id: 1,
itemType: {
warehouseFk: 1
- }
+ },
+ isPhotoRequested: true
};
const stock = {
visible: 1,
@@ -43,4 +44,16 @@ describe('vnItemDescriptor', () => {
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();
+ });
+ });
});
diff --git a/modules/item/front/diary/index.js b/modules/item/front/diary/index.js
index 9e104c8e6..03134913f 100644
--- a/modules/item/front/diary/index.js
+++ b/modules/item/front/diary/index.js
@@ -70,7 +70,8 @@ class Controller extends Section {
}
$onDestroy() {
- this.card.reload();
+ if (this.$state.getCurrentPath()[2].state.name === 'item')
+ this.card.reload();
}
}
diff --git a/modules/item/front/summary/index.html b/modules/item/front/summary/index.html
index 40e9c5aa7..46a2baef4 100644
--- a/modules/item/front/summary/index.html
+++ b/modules/item/front/summary/index.html
@@ -75,6 +75,15 @@
{{$ctrl.summary.item.itemType.worker.user.name}}
+
+
+
+
diff --git a/modules/item/front/waste/index/index.html b/modules/item/front/waste/index/index.html
index 5da5acbf1..f1475c1b3 100644
--- a/modules/item/front/waste/index/index.html
+++ b/modules/item/front/waste/index/index.html
@@ -33,16 +33,16 @@
-
+ ng-show="$ctrl.wasteConfig[detail.buyer].hidden">
{{::waste.family}}
{{::(waste.percentage / 100) | percentage: 2}}
{{::waste.dwindle | currency: 'EUR'}}
{{::waste.total | currency: 'EUR'}}
-
+
diff --git a/modules/mdb/back/methods/mdbVersion/upload.js b/modules/mdb/back/methods/mdbVersion/upload.js
index 864a73f52..58fc46abb 100644
--- a/modules/mdb/back/methods/mdbVersion/upload.js
+++ b/modules/mdb/back/methods/mdbVersion/upload.js
@@ -47,7 +47,16 @@ module.exports = Self => {
verb: 'POST'
}
});
- Self.upload = async(ctx, options) => {
+ Self.upload = async(
+ ctx,
+ appName,
+ toVersion,
+ branch,
+ fromVersion,
+ description,
+ unlock,
+ options
+ ) => {
const models = Self.app.models;
const myOptions = {};
const $t = ctx.req.__; // $translate
@@ -55,12 +64,6 @@ module.exports = Self => {
const AccessContainer = models.AccessContainer;
const fileOptions = {};
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')
Object.assign(myOptions, options);
@@ -153,11 +156,14 @@ module.exports = Self => {
formatDesc += `*${appName.toUpperCase()}* v.${toVersion} `;
const oldVersion = await models.MdbVersionTree.findOne({
- where: {version: fromVersion},
+ where: {
+ version: fromVersion,
+ app: appName
+ },
fields: ['branchFk']
}, myOptions);
- if (branch == oldVersion.branchFk)
+ if (!oldVersion || branch == oldVersion.branchFk)
formatDesc += `[*${branch}*]: `;
else
formatDesc += `[*${oldVersion.branchFk}* » *${branch}*]: `;
diff --git a/modules/route/front/summary/index.html b/modules/route/front/summary/index.html
index a64ad4ff7..56f7a49b9 100644
--- a/modules/route/front/summary/index.html
+++ b/modules/route/front/summary/index.html
@@ -93,7 +93,14 @@
{{ticket.priority | dashIfEmpty}}
{{ticket.street}}
- {{ticket.city}}
+
+ {{::ticket.city}}
+
{{ticket.postalCode}}
{
+ 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', {
diff --git a/modules/route/front/summary/index.spec.js b/modules/route/front/summary/index.spec.js
index ad300817a..cfa21aeb9 100644
--- a/modules/route/front/summary/index.spec.js
+++ b/modules/route/front/summary/index.spec.js
@@ -37,5 +37,29 @@ describe('Route', () => {
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');
+ });
+ });
});
});
diff --git a/modules/route/front/tickets/index.html b/modules/route/front/tickets/index.html
index 18d6fb160..32a4a2d7c 100644
--- a/modules/route/front/tickets/index.html
+++ b/modules/route/front/tickets/index.html
@@ -93,7 +93,14 @@
{{::ticket.street}}
- {{::ticket.city}}
+
+ {{::ticket.city}}
+
{{::ticket.postalCode}}
{
- if (!response.data)
+ this.$http.get(`Routes/${this.route.vehicleFk}/getDeliveryPoint`).then(res => {
+ if (!res.data)
throw new UserError(`The route's vehicle doesn't have a delivery point`);
- return response.data;
- }).then(address => {
- let addresses;
- if (address) addresses = address;
- let lines = this.getSelectedItems(this.tickets);
-
- let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
+ let addresses = res.data;
+ const lines = ticket ? [ticket] : this.getSelectedItems(this.tickets);
lines.forEach((line, index) => {
- const previusLine = lines[index - 1] ? lines[index - 1].street : null;
- if (previusLine != line.street)
+ const previousLine = lines[index - 1] ? lines[index - 1].street : null;
+ if (previousLine != 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');
});
}
diff --git a/modules/route/front/tickets/index.spec.js b/modules/route/front/tickets/index.spec.js
index f4a58154e..2c73048bd 100644
--- a/modules/route/front/tickets/index.spec.js
+++ b/modules/route/front/tickets/index.spec.js
@@ -136,7 +136,7 @@ describe('Route', () => {
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 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};
const url = `Routes/${controller.route.vehicleFk}/getDeliveryPoint`;
$httpBackend.expectGET(url).respond('46460 Av Espioca 100');
diff --git a/modules/supplier/back/methods/supplier/updateFiscalData.js b/modules/supplier/back/methods/supplier/updateFiscalData.js
index 4604b3f91..271ed8769 100644
--- a/modules/supplier/back/methods/supplier/updateFiscalData.js
+++ b/modules/supplier/back/methods/supplier/updateFiscalData.js
@@ -64,6 +64,14 @@ module.exports = Self => {
{
arg: 'healthRegister',
type: 'string'
+ },
+ {
+ arg: 'isVies',
+ type: 'boolean'
+ },
+ {
+ arg: 'isTrucker',
+ type: 'boolean'
}],
returns: {
arg: 'res',
diff --git a/modules/supplier/back/models/supplier.json b/modules/supplier/back/models/supplier.json
index 3cd6386a8..ee2c4fbbd 100644
--- a/modules/supplier/back/models/supplier.json
+++ b/modules/supplier/back/models/supplier.json
@@ -110,6 +110,9 @@
},
"healthRegister": {
"type": "string"
+ },
+ "isVies": {
+ "type": "boolean"
}
},
"relations": {
@@ -175,4 +178,4 @@
"foreignKey": "supplierActivityFk"
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/supplier/front/fiscal-data/index.html b/modules/supplier/front/fiscal-data/index.html
index a3ede2058..ccbd5b0d9 100644
--- a/modules/supplier/front/fiscal-data/index.html
+++ b/modules/supplier/front/fiscal-data/index.html
@@ -129,7 +129,7 @@
show-field="code"
rule>
- {{code}} - {{town.name}} ({{town.province.name}},
+ {{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.country}})
@@ -144,7 +144,7 @@
-
-
-
-
+
+
+
+
+
+
@@ -203,4 +210,4 @@
-
\ No newline at end of file
+
diff --git a/modules/ticket/back/methods/ticket/new.js b/modules/ticket/back/methods/ticket/new.js
index e6048421e..597ece3e5 100644
--- a/modules/ticket/back/methods/ticket/new.js
+++ b/modules/ticket/back/methods/ticket/new.js
@@ -111,7 +111,7 @@ module.exports = Self => {
args.landed = landedResult && landedResult.landed;
}
- query = `CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
+ query = `CALL vn.ticket_add(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
SELECT @result newTicketId;`;
const result = await Self.rawSql(query, [
args.clientId,
@@ -122,6 +122,7 @@ module.exports = Self => {
args.agencyModeId || null,
args.routeId || null,
args.landed,
+ true,
myUserId
], myOptions);
diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js
index cec8096a6..6ebc37d6e 100644
--- a/modules/ticket/back/methods/ticket/setDeleted.js
+++ b/modules/ticket/back/methods/ticket/setDeleted.js
@@ -42,6 +42,14 @@ module.exports = Self => {
if (!isEditable)
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
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
const sales = await models.Sale.find({
@@ -134,12 +142,12 @@ module.exports = Self => {
await models.TicketCollection.destroyById(ticketCollection.id, myOptions);
await Self.rawSql(`
- DELETE sc
+ DELETE sc
FROM vn.saleGroup sg
JOIN vn.sectorCollectionSaleGroup scsg ON scsg.saleGroupFk = sg.id
JOIN vn.sectorCollection sc ON sc.id = scsg.sectorCollectionFk
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
- JOIN vn.sale s ON s.id = sgd.saleFk
+ JOIN vn.sale s ON s.id = sgd.saleFk
WHERE s.ticketFk = ?;`, [ticket.id], myOptions);
if (tx) await tx.commit();
diff --git a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
index 1c17d4d0b..b2e70697a 100644
--- a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
@@ -101,4 +101,32 @@ describe('ticket setDeleted()', () => {
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');
+ });
});
diff --git a/modules/ticket/back/methods/ticket/specs/transferSales.spec.js b/modules/ticket/back/methods/ticket/specs/transferSales.spec.js
index cd2ce8599..270aae2f8 100644
--- a/modules/ticket/back/methods/ticket/specs/transferSales.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/transferSales.spec.js
@@ -80,34 +80,6 @@ describe('sale transferSales()', () => {
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() => {
const tx = await models.Ticket.beginTransaction({});
diff --git a/modules/ticket/back/methods/ticket/transferSales.js b/modules/ticket/back/methods/ticket/transferSales.js
index e268a1288..48035648c 100644
--- a/modules/ticket/back/methods/ticket/transferSales.js
+++ b/modules/ticket/back/methods/ticket/transferSales.js
@@ -77,9 +77,8 @@ module.exports = Self => {
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}}});
- if (hasClaimedSales && !isClaimManager)
+ if (hasClaimedSales)
throw new UserError(`Can't transfer claimed sales`);
for (const sale of sales) {
diff --git a/modules/ticket/front/sale-tracking/index.html b/modules/ticket/front/sale-tracking/index.html
index 8d3f414c2..33d22f92e 100644
--- a/modules/ticket/front/sale-tracking/index.html
+++ b/modules/ticket/front/sale-tracking/index.html
@@ -24,12 +24,44 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
{
+ 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;
+ }
+ };
+};
diff --git a/modules/worker/back/methods/worker/deallocatePDA.js b/modules/worker/back/methods/worker/deallocatePDA.js
new file mode 100644
index 000000000..7fa7855d1
--- /dev/null
+++ b/modules/worker/back/methods/worker/deallocatePDA.js
@@ -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;
+ }
+ };
+};
diff --git a/modules/worker/back/methods/worker/filter.js b/modules/worker/back/methods/worker/filter.js
index d08b27a18..71a8da96f 100644
--- a/modules/worker/back/methods/worker/filter.js
+++ b/modules/worker/back/methods/worker/filter.js
@@ -13,55 +13,59 @@ module.exports = Self => {
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
- }, {
- arg: 'tags',
- type: ['Object'],
- description: 'List of tags to filter with',
- http: {source: 'query'}
- }, {
+ },
+ {
arg: 'search',
type: 'String',
description: `If it's and integer searchs by id, otherwise it searchs by name`,
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'id',
type: 'Integer',
description: 'The worker id',
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'userFk',
type: 'Integer',
description: 'The user id',
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'fi',
type: 'String',
description: 'The worker fi',
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'departmentFk',
type: 'Integer',
description: 'The worker department id',
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'extension',
type: 'Integer',
description: 'The worker extension id',
http: {source: 'query'}
- }, {
+ },
+ {
arg: 'firstName',
type: 'String',
description: 'The worker firstName',
http: {source: 'query'}
- }, {
- arg: 'name',
+ },
+ {
+ arg: 'lastName',
type: 'String',
- description: 'The worker name',
+ description: 'The worker lastName',
http: {source: 'query'}
- }, {
- arg: 'nickname',
+ },
+ {
+ arg: 'userName',
type: 'String',
- description: 'The worker nickname',
+ description: 'The worker user name',
http: {source: 'query'}
}
],
@@ -93,10 +97,10 @@ module.exports = Self => {
return {'w.id': value};
case 'userFk':
return {'w.userFk': value};
- case 'name':
- return {'w.lastName': {like: `%${value}%`}};
case 'firstName':
return {'w.firstName': {like: `%${value}%`}};
+ case 'lastName':
+ return {'w.lastName': {like: `%${value}%`}};
case 'extension':
return {'p.extension': value};
case 'fi':
diff --git a/modules/worker/back/methods/worker/specs/allocatePDA.spec.js b/modules/worker/back/methods/worker/specs/allocatePDA.spec.js
new file mode 100644
index 000000000..5c80b6408
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/allocatePDA.spec.js
@@ -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();
+ });
+});
diff --git a/modules/worker/back/methods/worker/specs/deallocatePDA.spec.js b/modules/worker/back/methods/worker/specs/deallocatePDA.spec.js
new file mode 100644
index 000000000..bddd017cd
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/deallocatePDA.spec.js
@@ -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;
+ }
+ });
+});
diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json
index 7e03c8a23..8079bd165 100644
--- a/modules/worker/back/model-config.json
+++ b/modules/worker/back/model-config.json
@@ -20,6 +20,18 @@
"Device": {
"dataSource": "vn"
},
+ "DeviceProduction": {
+ "dataSource": "vn"
+ },
+ "DeviceProductionModels": {
+ "dataSource": "vn"
+ },
+ "DeviceProductionState": {
+ "dataSource": "vn"
+ },
+ "DeviceProductionUser": {
+ "dataSource": "vn"
+ },
"EducationLevel": {
"dataSource": "vn"
},
diff --git a/modules/worker/back/models/device-production-models.json b/modules/worker/back/models/device-production-models.json
new file mode 100644
index 000000000..dcf447520
--- /dev/null
+++ b/modules/worker/back/models/device-production-models.json
@@ -0,0 +1,15 @@
+{
+ "name": "DeviceProductionModels",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "deviceProductionModels"
+ }
+ },
+ "properties": {
+ "code": {
+ "type": "string",
+ "id": true
+ }
+ }
+}
diff --git a/modules/worker/back/models/device-production-state.json b/modules/worker/back/models/device-production-state.json
new file mode 100644
index 000000000..32695621a
--- /dev/null
+++ b/modules/worker/back/models/device-production-state.json
@@ -0,0 +1,18 @@
+{
+ "name": "DeviceProductionState",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "deviceProductionState"
+ }
+ },
+ "properties": {
+ "code": {
+ "type": "string",
+ "id": true
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+}
diff --git a/modules/worker/back/models/device-production-user.json b/modules/worker/back/models/device-production-user.json
new file mode 100644
index 000000000..568e79413
--- /dev/null
+++ b/modules/worker/back/models/device-production-user.json
@@ -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"
+ }
+ }
+}
diff --git a/modules/worker/back/models/device-production.json b/modules/worker/back/models/device-production.json
new file mode 100644
index 000000000..63672500b
--- /dev/null
+++ b/modules/worker/back/models/device-production.json
@@ -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"
+ }
+ }
+}
diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js
index e66259cd0..fa17640a8 100644
--- a/modules/worker/back/models/worker.js
+++ b/modules/worker/back/models/worker.js
@@ -14,4 +14,10 @@ module.exports = Self => {
require('../methods/worker/holidays')(Self);
require('../methods/worker/activeContract')(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'
+ });
};
diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json
index e3a941dd3..d21094f26 100644
--- a/modules/worker/back/models/worker.json
+++ b/modules/worker/back/models/worker.json
@@ -55,6 +55,9 @@
},
"code": {
"type" : "string"
+ },
+ "locker": {
+ "type" : "number"
}
},
"relations": {
diff --git a/modules/worker/front/basic-data/index.html b/modules/worker/front/basic-data/index.html
index 5a3acdde5..d89d88f2e 100644
--- a/modules/worker/front/basic-data/index.html
+++ b/modules/worker/front/basic-data/index.html
@@ -11,7 +11,7 @@
@@ -24,14 +24,14 @@
@@ -73,11 +73,16 @@
+
+
diff --git a/modules/worker/front/basic-data/locale/es.yml b/modules/worker/front/basic-data/locale/es.yml
index a83c30d99..edf08de90 100644
--- a/modules/worker/front/basic-data/locale/es.yml
+++ b/modules/worker/front/basic-data/locale/es.yml
@@ -5,4 +5,5 @@ SSN: NSS
Married: Casado/a
Single: Soltero/a
Business phone: Teléfono de empresa
-Mobile extension: Extensión móvil
\ No newline at end of file
+Mobile extension: Extensión móvil
+Locker: Taquilla
diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js
index 97126407c..657f6a8c6 100644
--- a/modules/worker/front/index.js
+++ b/modules/worker/front/index.js
@@ -10,6 +10,7 @@ import './descriptor-popover';
import './search-panel';
import './basic-data';
import './pbx';
+import './pda';
import './department';
import './calendar';
import './time-control';
diff --git a/modules/worker/front/locale/es.yml b/modules/worker/front/locale/es.yml
index 672f4c52f..b5bcfefa4 100644
--- a/modules/worker/front/locale/es.yml
+++ b/modules/worker/front/locale/es.yml
@@ -23,4 +23,11 @@ worker: trabajador
Go to the worker: Ir al trabajador
Click to exclude the user from getting disabled: Marcar para no deshabilitar
Click to allow the user to be disabled: Marcar para deshabilitar
-This user can't be disabled: Fijado para no deshabilitar
\ No newline at end of file
+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
diff --git a/modules/worker/front/pda/index.html b/modules/worker/front/pda/index.html
new file mode 100644
index 000000000..2f1626ba8
--- /dev/null
+++ b/modules/worker/front/pda/index.html
@@ -0,0 +1,49 @@
+
+
diff --git a/modules/worker/front/pda/index.js b/modules/worker/front/pda/index.js
new file mode 100644
index 000000000..885261e5c
--- /dev/null
+++ b/modules/worker/front/pda/index.js
@@ -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,
+});
diff --git a/modules/worker/front/pda/index.spec.js b/modules/worker/front/pda/index.spec.js
new file mode 100644
index 000000000..a0540af45
--- /dev/null
+++ b/modules/worker/front/pda/index.spec.js
@@ -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('');
+ 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();
+ });
+ });
+ });
+});
diff --git a/modules/worker/front/pda/style.scss b/modules/worker/front/pda/style.scss
new file mode 100644
index 000000000..4d9d70953
--- /dev/null
+++ b/modules/worker/front/pda/style.scss
@@ -0,0 +1,6 @@
+span.separator{
+ border-left: 1px solid black;
+ height: 100%;
+ margin: 0 10px;
+}
+
diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json
index dad55512b..64b98bfca 100644
--- a/modules/worker/front/routes.json
+++ b/modules/worker/front/routes.json
@@ -11,9 +11,10 @@
],
"card": [
{"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.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"},
{
"icon": "icon-wiki",
@@ -141,6 +142,13 @@
"component": "vn-worker-create",
"description": "New worker",
"acl": ["hr"]
+ },
+ {
+ "url": "/pda",
+ "state": "worker.card.pda",
+ "component": "vn-worker-pda",
+ "description": "PDA",
+ "acl": ["hr", "productionAssi"]
}
]
}
diff --git a/modules/worker/front/search-panel/index.html b/modules/worker/front/search-panel/index.html
index ca57722c8..2adb56587 100644
--- a/modules/worker/front/search-panel/index.html
+++ b/modules/worker/front/search-panel/index.html
@@ -30,7 +30,7 @@
+ ng-model="filter.lastName">
diff --git a/modules/worker/front/summary/index.html b/modules/worker/front/summary/index.html
index 0a6bec822..72b8e729a 100644
--- a/modules/worker/front/summary/index.html
+++ b/modules/worker/front/summary/index.html
@@ -11,23 +11,23 @@
-
Basic data
-
-
-
-
-
-
+
+
+
User data
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/modules/worker/front/summary/locale/es.yml b/modules/worker/front/summary/locale/es.yml
index e9c8e5583..fb9d2e2ca 100644
--- a/modules/worker/front/summary/locale/es.yml
+++ b/modules/worker/front/summary/locale/es.yml
@@ -1,3 +1,4 @@
Business phone: Teléfono de empresa
Personal phone: Teléfono personal
-Mobile extension: Extensión móvil
\ No newline at end of file
+Mobile extension: Extensión móvil
+Locker: Taquilla
diff --git a/package.json b/package.json
index f4415f99b..30c8039ac 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "23.08.01",
+ "version": "23.10.01",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",
diff --git a/print/templates/reports/collection-label/collection-label.js b/print/templates/reports/collection-label/collection-label.js
index 656dde082..d45ecb2bc 100644
--- a/print/templates/reports/collection-label/collection-label.js
+++ b/print/templates/reports/collection-label/collection-label.js
@@ -66,8 +66,10 @@ module.exports = {
else
value = '---';
- if (labelData.routeFk)
- value = `${value} [${labelData.routeFk.toString().substring(0, 3)}]`;
+ if (labelData.routeFk) {
+ let routeFk = labelData.routeFk.toString();
+ value = `${value} [${routeFk.substring(routeFk.length - 3)}]`;
+ }
return value;
},
diff --git a/print/templates/reports/collection-label/options.json b/print/templates/reports/collection-label/options.json
index a555c5723..ad5ad4750 100644
--- a/print/templates/reports/collection-label/options.json
+++ b/print/templates/reports/collection-label/options.json
@@ -3,9 +3,9 @@
"height": "4.9cm",
"margin": {
"top": "0.3cm",
- "right": "0.6cm",
+ "right": "0.3cm",
"bottom": "0cm",
- "left": "0cm"
+ "left": "0.2cm"
},
"printBackground": true
}
\ No newline at end of file