Merge branch 'dev' into test
gitea/salix/test This commit looks good Details

This commit is contained in:
Joan Sanchez 2019-05-01 18:57:25 +02:00
commit 462abce981
193 changed files with 3025 additions and 1162 deletions

View File

@ -31,3 +31,4 @@ rules:
curly: [error, multi-or-nest] curly: [error, multi-or-nest]
indent: [error, 4] indent: [error, 4]
arrow-parens: [error, as-needed] arrow-parens: [error, as-needed]
jasmine/no-focused-tests: 0

View File

@ -0,0 +1,76 @@
const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra');
module.exports = Self => {
Self.remoteMethodCtx('download', {
description: 'Download a document',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'String',
description: 'The document id',
http: {source: 'path'}
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
}, {
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
}, {
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: `/:id/download`,
verb: 'GET'
}
});
Self.download = async function(ctx, id) {
const userId = ctx.req.accessToken.userId;
const env = process.env.NODE_ENV;
const document = await Self.findById(id, {
include: {
relation: 'dmsType',
scope: {
fields: ['path', 'readRoleFk'],
include: {
relation: 'readRole'
}
}
}
});
const readRole = document.dmsType().readRole().name;
const hasRequiredRole = await Self.app.models.Account.hasRole(userId, readRole);
if (!hasRequiredRole)
throw new UserError(`You don't have enough privileges`);
if (env && env != 'development') {
const path = `/${document.companyFk}/${document.dmsType().path}/${document.file}`;
file = {
path: `/var/lib/salix/dms/${path}`,
contentType: 'application/octet-stream',
name: document.file
};
} else {
file = {
path: `${process.cwd()}/README.md`,
contentType: 'text/plain',
name: `README.md`
};
}
await fs.access(file.path);
let stream = fs.createReadStream(file.path);
return [stream, file.contentType, `filename="${file.name}"`];
};
};

View File

@ -0,0 +1,30 @@
const app = require('vn-loopback/server/server');
/**
* Pendiente de fixtures dms, dmsType, ticketDms
* CAU: 10728
*/
xdescribe('dms download()', () => {
let dmsId = 1;
it('should return a response for an employee with text content-type', async() => {
let workerFk = 107;
let ctx = {req: {accessToken: {userId: workerFk}}};
const result = await app.models.Dms.download(ctx, dmsId);
expect(result[1]).toEqual('text/plain');
});
it(`should return an error for a user without enough privileges`, async() => {
let clientId = 101;
let ctx = {req: {accessToken: {userId: clientId}}};
let error;
await app.models.Dms.download(ctx, dmsId).catch(e => {
error = e;
}).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`);
});
expect(error).toBeDefined();
});
});

View File

@ -37,6 +37,12 @@
}, },
"EmailUser": { "EmailUser": {
"dataSource": "vn" "dataSource": "vn"
},
"Dms": {
"dataSource": "vn"
},
"DmsType": {
"dataSource": "vn"
} }
} }

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

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

51
back/models/dms.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "Dms",
"description": "Documental Managment system",
"base": "VnModel",
"options": {
"mysql": {
"table": "dms"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"file": {
"type": "string"
},
"reference": {
"type": "string"
},
"description": {
"type": "string"
},
"created": {
"type": "Date"
}
},
"relations": {
"dmsType": {
"type": "belongsTo",
"model": "DmsType",
"foreignKey": "dmsTypeFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
},
"company": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyFk"
}
}
}

43
back/models/dmsType.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "DmsType",
"description": "Documental Managment system types",
"base": "VnModel",
"options": {
"mysql": {
"table": "dmsType"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "string",
"required": true
},
"path": {
"type": "string",
"required": true
}
},
"relations": {
"readRole": {
"type": "belongsTo",
"model": "Role",
"foreignKey": "readRoleFk"
},
"writeRole": {
"type": "belongsTo",
"model": "Role",
"foreignKey": "writeRoleFk"
}
},
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -0,0 +1,5 @@
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (162, 'InvoiceOut', 'delete', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (163, 'InvoiceOut', 'book', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (164, 'InvoiceOut', 'regenerate', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (165, 'TicketDms', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (166, 'Dms', 'download', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,20 @@
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`dms` AS
SELECT
`g`.`id` AS `id`,
`g`.`gesttip_id` AS `dmsTypeFk`,
`g`.`file` AS `file`,
`g`.`trabajador_id` AS `workerFk`,
`g`.`warehouse_id` AS `warehouseFk`,
`g`.`emp_id` AS `companyFk`,
`g`.`orden` AS `priority`,
`g`.`file` AS `hasFile`,
`g`.`sref` AS `reference`,
`g`.`brief` AS `description`,
`g`.`odbc_date` AS `created`
FROM
`vn2008`.`gestdoc` `g`

View File

@ -0,0 +1,71 @@
DROP procedure IF EXISTS `nst`.`nodeAdd`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeAdd`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45), IN `vParentFk` INT, IN `vChild` VARCHAR(100))
BEGIN
DECLARE vSql TEXT;
DECLARE vTableClone VARCHAR(45);
SET vTableClone = CONCAT(vTable, 'Clone');
CALL util.exec(CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp.', vTableClone));
CALL util.exec(CONCAT(
'CREATE TEMPORARY TABLE tmp.', vTableClone,
' ENGINE = MEMORY',
' SELECT * FROM ', vScheme, '.', vTable
));
CALL util.exec(CONCAT(
'SELECT COUNT(c.id) INTO @childs',
' FROM ', vScheme, '.', vTable, ' p',
' LEFT JOIN tmp.', vTableClone, ' c ON c.lft',
' BETWEEN p.lft AND p.rgt AND c.id != ', vParentFk,
' WHERE p.id = ', vParentFk
));
IF @childs = 0 THEN
CALL util.exec(CONCAT(
'SELECT lft INTO @vLeft',
' FROM ', vScheme, '.', vTable,
' WHERE id = ', vParentFk
));
ELSE
CALL util.exec(CONCAT(
'SELECT c.rgt INTO @vLeft',
' FROM ', vScheme, '.', vTable, ' p',
' JOIN tmp.', vTableClone, ' c ON c.lft BETWEEN p.lft AND p.rgt',
' WHERE p.id = ', vParentFk,
' ORDER BY c.lft',
' DESC LIMIT 1'
));
END IF;
CALL util.exec(CONCAT(
'UPDATE ', vScheme, '.', vTable, ' SET rgt = rgt + 2',
' WHERE rgt > @vLeft',
' ORDER BY rgt DESC'
));
CALL util.exec(CONCAT(
'UPDATE ', vScheme, '.', vTable, ' SET lft = lft + 2',
' WHERE lft > @vLeft',
' ORDER BY lft DESC'
));
SET vChild = REPLACE(vChild, "'", "\\'");
CALL util.exec(CONCAT(
'INSERT INTO ', vScheme, '.', vTable, ' (name, lft, rgt)',
' VALUES ("', vChild, '", @vLeft + 1, @vLeft + 2)'
));
CALL util.exec(CONCAT(
'SELECT id, name, lft, rgt, depth, sons',
' FROM ', vScheme, '.', vTable,
' WHERE id = LAST_INSERT_ID()'
));
CALL util.exec(CONCAT('DROP TEMPORARY TABLE tmp.', vTableClone));
END$$
DELIMITER ;

View File

@ -0,0 +1,34 @@
DROP procedure IF EXISTS `nst`.`nodeDelete`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeDelete`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45), IN `vNodeId` INT)
BEGIN
DECLARE vMyRight INT;
DECLARE vMyLeft INT;
DECLARE vMyWidth INT;
CALL util.exec(CONCAT(
'SELECT t.rgt, t.lft, t.rgt - t.lft + 1',
' INTO @vMyRight, @vMyLeft, @vMyWidth',
' FROM ', vScheme, '.', vTable, ' t',
' WHERE t.id = ', vNodeId
));
CALL util.exec(CONCAT(
'DELETE FROM ', vScheme, '.', vTable,
' WHERE lft BETWEEN @vMyLeft AND @vMyRight'
));
CALL util.exec(CONCAT(
'UPDATE ', vScheme, '.', vTable, ' SET rgt = rgt - @vMyWidth'
' WHERE rgt > @vMyRight ORDER BY rgt'
));
CALL util.exec(CONCAT(
'UPDATE ', vScheme, '.', vTable, ' SET lft = lft - @vMyWidth'
' WHERE lft > @vMyRight ORDER BY lft'
));
END$$
DELIMITER ;

View File

@ -0,0 +1,23 @@
DROP procedure IF EXISTS `nst`.`nodeRecalc`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeRecalc`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45))
BEGIN
CALL util.exec(CONCAT (
'UPDATE ', vScheme, '.', vTable, ' d',
' JOIN (SELECT',
' node.id,',
' COUNT(parent.id) - 1 as depth,',
' cast((node.rgt - node.lft - 1) / 2 as DECIMAL) as sons',
' FROM ',
' ', vScheme, '.', vTable, ' AS node,',
' ', vScheme, '.', vTable, ' AS parent',
' WHERE node.lft BETWEEN parent.lft AND parent.rgt',
' GROUP BY node.id',
' ORDER BY node.lft) n ON n.id = d.id ',
' SET d.`depth` = n.depth, d.sons = n.sons'
));
END$$
DELIMITER ;

View File

@ -0,0 +1,12 @@
DROP VIEW IF EXISTS `vn`.`dmsTicket` ;
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`ticketDms` AS
SELECT
`g`.`Id_Ticket` AS `ticketFk`,
`g`.`gestdoc_id` AS `dmsFk`
FROM
`vn2008`.`tickets_gestdoc` `g`;

View File

@ -0,0 +1,7 @@
ALTER TABLE `vn2008`.`gesttip`
ADD COLUMN `writeRoleFk` INT(10) UNSIGNED NULL AFTER `path`,
ADD COLUMN `readRoleFk` INT(10) UNSIGNED NULL AFTER `writeRoleFk`,
ADD CONSTRAINT `readRoleFk` FOREIGN KEY (`readRoleFk`) REFERENCES `account`.`role` (`id`),
ADD CONSTRAINT `writeRoleFk` FOREIGN KEY (`writeRoleFk`) REFERENCES `account`.`role` (`id`);
UPDATE `vn2008`.`gesttip` SET `readRoleFk`='1' WHERE `id`='14';

View File

@ -0,0 +1,13 @@
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`dmsType` AS
SELECT
`g`.`id` AS `id`,
`g`.`tipo` AS `name`,
`g`.`path` AS `path`,
`g`.`readRoleFk` AS `readRoleFk`,
`g`.`writeRoleFk` AS `writeRoleFk`
FROM
`vn2008`.`gesttip` `g`;

View File

@ -388,11 +388,11 @@ INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`,
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `pdf`) INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `pdf`)
VALUES VALUES
( 1, 'T', 500 , DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 101, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), ( 1, 'T', 156.09, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 101, CURDATE(), 442, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), CURDATE(), 1, 1),
( 2, 'T', 350.50 , DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 102, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), ( 2, 'T', 208.35, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 102, CURDATE(), 442, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), CURDATE(), 1, 1),
( 3, 'T', 90.30 , CURDATE(), 103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), ( 3, 'T', 20.02, CURDATE(), 103, CURDATE(), 442, CURDATE(), null, 1, 1),
( 4, 'T', 290.30 , DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), ( 4, 'T', 20.02, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 103, CURDATE(), 442, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), null, 1, 1),
( 5, 'A', 190.30 , DATE_ADD(CURDATE(), INTERVAL +2 MONTH), 103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1); ( 5, 'A', 20.22, DATE_ADD(CURDATE(), INTERVAL +2 MONTH), 103, CURDATE(), 442, DATE_ADD(CURDATE(), INTERVAL +2 MONTH), null, 1, 1);
UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1; UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1;
UPDATE `vn`.`invoiceOut` SET ref = 'T2222222' WHERE id = 2; UPDATE `vn`.`invoiceOut` SET ref = 'T2222222' WHERE id = 2;
@ -413,6 +413,19 @@ INSERT INTO `vn`.`invoiceOutTax` (`invoiceOutFk`, `taxableBase`, `vat`, `pgcFk`)
(5, 100, 10, 4722000010), (5, 100, 10, 4722000010),
(5, 200, 21, 4722000021); (5, 200, 21, 4722000021);
INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`)
VALUES
('CEE', 1, 10),
('EQU', 0, 1),
('NATIONAL', 0, 1),
('WORLD', 2, 15);
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`)
VALUES
('A', 'Global nacional', 1, 'NATIONAL', 0),
('T', 'Española rapida', 1, 'NATIONAL', 0),
('V', 'Intracomunitaria global', 0, 'CEE', 1);
INSERT INTO `vn`.`ticket`(`id`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `created`) INSERT INTO `vn`.`ticket`(`id`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `created`)
VALUES VALUES
(1 , 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY) , DATE_ADD(CURDATE(), INTERVAL -15 DAY) , 101, 'address 21', 121, 'T1111111', 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY) ), (1 , 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY) , DATE_ADD(CURDATE(), INTERVAL -15 DAY) , 101, 'address 21', 121, 'T1111111', 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY) ),
@ -423,8 +436,8 @@ INSERT INTO `vn`.`ticket`(`id`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped
(6 , 3, 3, 4, DATE_ADD(CURDATE(), INTERVAL -2 DAY) , DATE_ADD(CURDATE(), INTERVAL -2 DAY) , 103, 'address 23', 123, 'T4444444', 0, DATE_ADD(CURDATE(), INTERVAL -2 DAY) ), (6 , 3, 3, 4, DATE_ADD(CURDATE(), INTERVAL -2 DAY) , DATE_ADD(CURDATE(), INTERVAL -2 DAY) , 103, 'address 23', 123, 'T4444444', 0, DATE_ADD(CURDATE(), INTERVAL -2 DAY) ),
(7 , 4, 4, 4, DATE_ADD(CURDATE(), INTERVAL -1 DAY) , DATE_ADD(CURDATE(), INTERVAL -1 DAY) , 104, 'address 24', 124, 'T4444444', 0, DATE_ADD(CURDATE(), INTERVAL -1 DAY) ), (7 , 4, 4, 4, DATE_ADD(CURDATE(), INTERVAL -1 DAY) , DATE_ADD(CURDATE(), INTERVAL -1 DAY) , 104, 'address 24', 124, 'T4444444', 0, DATE_ADD(CURDATE(), INTERVAL -1 DAY) ),
(8 , 1, 1, 4, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 104, 'address 24', 124, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH) ), (8 , 1, 1, 4, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 104, 'address 24', 124, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH) ),
(9 , 5, 5, 4, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 105, 'address 25', 125, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -2 MONTH) ), (9 , 5, 5, 4, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 105, 'address 25', 125, 'A1111111', 0, DATE_ADD(CURDATE(), INTERVAL -2 MONTH) ),
(10, 6, 5, 5, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(CURDATE(), INTERVAL -3 MONTH), 105, 'address 25', 125, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -3 MONTH) ), (10, 6, 5, 5, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(CURDATE(), INTERVAL -3 MONTH), 105, 'address 25', 125, 'A1111111', 0, DATE_ADD(CURDATE(), INTERVAL -3 MONTH) ),
(11, 7, 1, 1, CURDATE() , CURDATE() , 101, 'address 21', 121, NULL, 0, CURDATE() ), (11, 7, 1, 1, CURDATE() , CURDATE() , 101, 'address 21', 121, NULL, 0, CURDATE() ),
(12, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 101, 'address 21', 121, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH) ), (12, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 101, 'address 21', 121, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH) ),
(13, 2, 2, 2, DATE_ADD(CURDATE(), INTERVAL +2 MONTH), DATE_ADD(CURDATE(), INTERVAL +2 MONTH), 101, 'address 21', 121, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +2 MONTH) ), (13, 2, 2, 2, DATE_ADD(CURDATE(), INTERVAL +2 MONTH), DATE_ADD(CURDATE(), INTERVAL +2 MONTH), 101, 'address 21', 121, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +2 MONTH) ),
@ -614,22 +627,22 @@ INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`,
INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `price`, `discount`, `reserved`, `isPicked`, `created`) INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `price`, `discount`, `reserved`, `isPicked`, `created`)
VALUES VALUES
(1, 1, 1, 'Ranged weapon longbow 2m', 5, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (1, 1, 1, 'Ranged weapon longbow 2m', 5, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(2, 2, 1, 'Melee weapon combat first 15cm', 10, 1.07, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (2, 2, 1, 'Melee weapon combat fist 15cm', 10, 1.07, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(3, 1, 1, 'Ranged weapon longbow 2m', 2, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (3, 1, 1, 'Ranged weapon longbow 2m', 2, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(4, 4, 1, 'Melee weapon heavy shield 1x0.5m', 20, 3.06, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (4, 4, 1, 'Melee weapon heavy shield 1x0.5m', 20, 3.06, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(5, 1, 2, 'Ranged weapon longbow 2m', 10, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -10 DAY)), (5, 1, 2, 'Ranged weapon longbow 2m', 10, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -10 DAY)),
(6, 1, 3, 'Ranged weapon longbow 2m', 15, 6.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -5 DAY)), (6, 1, 3, 'Ranged weapon longbow 2m', 15, 6.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -5 DAY)),
(7, 2, 11, 'Melee weapon combat first 15cm', 15, 1.46, 0, 0, 0, CURDATE()), (7, 2, 11, 'Melee weapon combat fist 15cm', 15, 1.46, 0, 0, 0, CURDATE()),
(8, 4, 11, 'Melee weapon heavy shield 1x0.5m', 10, 3.04, 0, 0, 0, CURDATE()), (8, 4, 11, 'Melee weapon heavy shield 1x0.5m', 10, 3.04, 0, 0, 0, CURDATE()),
(9, 1, 16, 'Ranged weapon longbow 2m', 5, 9.10, 0, 0, 0, CURDATE()), (9, 1, 16, 'Ranged weapon longbow 2m', 5, 9.10, 0, 0, 0, CURDATE()),
(10, 2, 16, 'Melee weapon combat first 15cm', 10, 1.07, 0, 0, 0, CURDATE()), (10, 2, 16, 'Melee weapon combat fist 15cm', 10, 1.07, 0, 0, 0, CURDATE()),
(11, 1, 16, 'Ranged weapon longbow 2m', 2, 9.10, 0, 0, 0, CURDATE()), (11, 1, 16, 'Ranged weapon longbow 2m', 2, 9.10, 0, 0, 0, CURDATE()),
(12, 4, 16, 'Melee weapon heavy shield 1x0.5m', 20, 3.06, 0, 0, 0, CURDATE()), (12, 4, 16, 'Melee weapon heavy shield 1x0.5m', 20, 3.06, 0, 0, 0, CURDATE()),
(13, 2, 8, 'Melee weapon combat first 15cm', 15, 1.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), (13, 2, 8, 'Melee weapon combat fist 15cm', 15, 1.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
(14, 1, 8, 'Ranged weapon longbow 2m', 10, 2.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), (14, 1, 8, 'Ranged weapon longbow 2m', 10, 2.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
(15, 1, 19, 'Ranged weapon longbow 2m', 10, 1.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), (15, 1, 19, 'Ranged weapon longbow 2m', 10, 1.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)),
(16, 2, 20, 'Melee weapon combat first 15cm', 15, 1.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), (16, 2, 20, 'Melee weapon combat fist 15cm', 15, 1.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)),
(17, 2, 22, 'Melee weapon combat first 15cm', 30, 2.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), (17, 2, 22, 'Melee weapon combat fist 15cm', 30, 2.30, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)),
(18, 4, 22, 'Melee weapon heavy shield 1x0.5m', 20, 3.00, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), (18, 4, 22, 'Melee weapon heavy shield 1x0.5m', 20, 3.00, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL +1 DAY)),
(19, 1, 4, 'Ranged weapon longbow 2m', 1, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -4 DAY)), (19, 1, 4, 'Ranged weapon longbow 2m', 1, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -4 DAY)),
(20, 1, 5, 'Ranged weapon longbow 2m', 1, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -3 DAY)), (20, 1, 5, 'Ranged weapon longbow 2m', 1, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -3 DAY)),
@ -816,7 +829,7 @@ INSERT INTO `vn`.`itemTag`(`id`,`itemFk`,`tagFk`,`value`,`priority`)
(6 , 1, 67, 'precission', 6), (6 , 1, 67, 'precission', 6),
(7 , 1, 23, '1', 7), (7 , 1, 23, '1', 7),
(8 , 2, 56, 'Melee weapon', 1), (8 , 2, 56, 'Melee weapon', 1),
(9 , 2, 58, 'combat first', 2), (9 , 2, 58, 'combat fist', 2),
(10, 2, 27, '15cm', 3), (10, 2, 27, '15cm', 3),
(11, 2, 36, 'Stark Industries', 4), (11, 2, 36, 'Stark Industries', 4),
(12, 2, 1, 'Silver', 5), (12, 2, 1, 'Silver', 5),
@ -1213,13 +1226,15 @@ INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `atenderFk`
(1, 'Ranged weapon longbow 2m', 18, 35, 5, 1, 9.10, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (1, 'Ranged weapon longbow 2m', 18, 35, 5, 1, 9.10, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(2, 'Melee weapon combat first 15cm', 18, 35, 10, 2, 1.07, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (2, 'Melee weapon combat first 15cm', 18, 35, 10, 2, 1.07, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(3, 'Melee weapon heavy shield 1x0.5m', 18, 35, 20, 4, 3.06, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (3, 'Melee weapon heavy shield 1x0.5m', 18, 35, 20, 4, 3.06, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),
(4, 'Melee weapon combat first 15cm', 18, 35, 15, 2, 1.30, NULL, NULL, 11, CURDATE()); (4, 'Melee weapon combat first 15cm', 18, 35, 15, NULL, 1.30, NULL, NULL, 11, CURDATE());
INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`) INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`)
VALUES VALUES
(1, 'delivery charge', 1, 2.00, 1, 1), (1, 'Documentos', 1, 2.00, 1, 1),
(2, 'training course', 1, 10.00, 1, 2), (2, 'Porte Agencia', 1, 10.00, 1, 2),
(3, 'delivery charge', 1, 5.50, 1, 11); (3, 'Documentos', 1, 5.50, 1, 11),
(4, 'Documentos', 1, 2.00, 1, 9),
(5, 'Documentos', 1, 2.00, 1, 8);
INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `warehouseFk`, `agencyModeFk`, `travelingDays`, `price`, `bonus`) INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `warehouseFk`, `agencyModeFk`, `travelingDays`, `price`, `bonus`)
@ -1260,7 +1275,7 @@ INSERT INTO `pbx`.`sip`(`user_id`, `extension`, `secret`, `caller_id`)
(9, 1201, '123456', 'developer'); (9, 1201, '123456', 'developer');
INSERT INTO `postgresql`.`person`(`person_id`, `name`, `nickname`, `nif`, `firstname`, `id_trabajador`) INSERT INTO `postgresql`.`person`(`person_id`, `name`, `nickname`, `nif`, `firstname`, `id_trabajador`)
SELECT w.id, w.name, u.nickname,CONCAT(RPAD(CONCAT(w.id,9),8,w.id),'A'),w.firstName,w.id SELECT w.id, w.lastName, u.nickname,CONCAT(RPAD(CONCAT(w.id,9),8,w.id),'A'),w.firstName,w.id
FROM `vn`.`worker` `w` FROM `vn`.`worker` `w`
JOIN `account`.`user` `u` ON `u`.`id` = `w`.`userFk`; JOIN `account`.`user` `u` ON `u`.`id` = `w`.`userFk`;
@ -1273,7 +1288,7 @@ INSERT INTO `postgresql`.`profile`(`profile_id`, `person_id`, `profile_type_id`)
FROM `postgresql`.`person` `p`; FROM `postgresql`.`person` `p`;
INSERT INTO `postgresql`.`business`(`business_id`, `client_id`, `provider_id`, `date_start`, `date_end`, `workerBusiness`, `reasonEndFk`) INSERT INTO `postgresql`.`business`(`business_id`, `client_id`, `provider_id`, `date_start`, `date_end`, `workerBusiness`, `reasonEndFk`)
SELECT p.profile_id, p.profile_id, 1000, DATE_ADD(CURDATE(), INTERVAL -15 DAY), DATE_ADD(CURDATE(), INTERVAL +6 MONTH), CONCAT('E-46-',RPAD(CONCAT(p.profile_id,9),8,p.profile_id)), NULL SELECT p.profile_id, p.profile_id, 1000, CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-31'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL +1 YEAR)), '-01-01'), CONCAT('E-46-',RPAD(CONCAT(p.profile_id,9),8,p.profile_id)), NULL
FROM `postgresql`.`profile` `p`; FROM `postgresql`.`profile` `p`;
INSERT INTO `postgresql`.`business_labour`(`business_id`, `notes`, `department_id`, `professional_category_id`, `incentivo`, `calendar_labour_type_id`, `porhoras`, `labour_agreement_id`, `workcenter_id`) INSERT INTO `postgresql`.`business_labour`(`business_id`, `notes`, `department_id`, `professional_category_id`, `incentivo`, `calendar_labour_type_id`, `porhoras`, `labour_agreement_id`, `workcenter_id`)

View File

@ -19,3 +19,4 @@ services:
volumes: volumes:
- /containers/salix:/etc/salix - /containers/salix:/etc/salix
- /mnt/storage/pdfs:/var/lib/salix/pdfs - /mnt/storage/pdfs:/var/lib/salix/pdfs
- /mnt/storage/dms:/var/lib/salix/dms

View File

@ -1,6 +1,3 @@
// eslint max-len: ["error", 500]
// eslint key-spacing: ["error", 500]
export default { export default {
vnTextfield: 'vn-textfield > div > div > div > input', vnTextfield: 'vn-textfield > div > div > div > input',
vnInputNumber: 'vn-input-number > div > div > div > input', vnInputNumber: 'vn-input-number > div > div > div > input',

View File

@ -2,30 +2,30 @@ import components from './components_selectors.js';
export default { export default {
globalItems: { globalItems: {
applicationsMenuButton: `#apps`, applicationsMenuButton: '#apps',
applicationsMenuVisible: `vn-main-menu [vn-id="apps-menu"] ul`, applicationsMenuVisible: 'vn-main-menu [vn-id="apps-menu"] ul',
clientsButton: `vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="client.index"]`, clientsButton: 'vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="client.index"]',
itemsButton: `vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="item.index"]`, itemsButton: 'vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="item.index"]',
ticketsButton: `vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="ticket.index"]`, ticketsButton: 'vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="ticket.index"]',
claimsButton: `vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="claim.index"]`, claimsButton: 'vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="claim.index"]',
returnToModuleIndexButton: `a[translate-attr="{title: 'Return to module index'}"]`, returnToModuleIndexButton: `a[translate-attr="{title: 'Return to module index'}"]`,
userMenuButton: `vn-topbar #user`, userMenuButton: 'vn-topbar #user',
userLocalWarehouse: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localWarehouseFk"]`, userLocalWarehouse: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localWarehouseFk"]',
userLocalBank: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localBankFk"]`, userLocalBank: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localBankFk"]',
userLocalCompany: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localCompanyFk"]`, userLocalCompany: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localCompanyFk"]',
userWarehouse: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.warehouseFk"]`, userWarehouse: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.warehouseFk"]',
userCompany: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.companyFk"]`, userCompany: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.companyFk"]',
userConfigFirstAutocompleteClear: '#localWarehouse > div > div > div > vn-icon.clear', userConfigFirstAutocompleteClear: '#localWarehouse > div > div > div > vn-icon.clear',
userConfigSecondAutocompleteClear: '#localBank > div > div > div > vn-icon.clear', userConfigSecondAutocompleteClear: '#localBank > div > div > div > vn-icon.clear',
userConfigThirdAutocompleteClear: '#localCompany > div > div > div > vn-icon.clear', userConfigThirdAutocompleteClear: '#localCompany > div > div > div > vn-icon.clear',
acceptVnConfirm: `vn-confirm button[response=ACCEPT]` acceptVnConfirm: 'vn-confirm button[response=ACCEPT]'
}, },
clientsIndex: { clientsIndex: {
searchClientInput: `${components.vnTextfield}`, searchClientInput: `${components.vnTextfield}`,
searchButton: `vn-searchbar vn-icon[icon="search"]`, searchButton: 'vn-searchbar vn-icon[icon="search"]',
searchResult: `vn-client-index .vn-list-item`, searchResult: 'vn-client-index .vn-list-item',
createClientButton: `${components.vnFloatButton}`, createClientButton: `${components.vnFloatButton}`,
othersButton: `vn-left-menu li[name="Others"] > a` othersButton: 'vn-left-menu li[name="Others"] > a'
}, },
createClientView: { createClientView: {
name: `${components.vnTextfield}[name="name"]`, name: `${components.vnTextfield}[name="name"]`,
@ -37,53 +37,53 @@ export default {
email: `${components.vnTextfield}[name="email"]`, email: `${components.vnTextfield}[name="email"]`,
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`, salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
createButton: `${components.vnSubmit}`, createButton: `${components.vnSubmit}`,
cancelButton: `vn-button[href="#!/client/index"]` cancelButton: 'vn-button[href="#!/client/index"]'
}, },
clientDescriptor: { clientDescriptor: {
moreMenu: `vn-client-descriptor vn-icon-menu > div > vn-icon`, moreMenu: 'vn-client-descriptor vn-icon-menu > div > vn-icon',
simpleTicketButton: 'vn-client-descriptor vn-popover > div > div.content > div > div.list > ul > li' simpleTicketButton: 'vn-client-descriptor vn-popover > div > div.content > div > div.list > ul > li'
}, },
clientBasicData: { clientBasicData: {
basicDataButton: `vn-left-menu a[ui-sref="client.card.basicData"]`, basicDataButton: 'vn-left-menu a[ui-sref="client.card.basicData"]',
nameInput: `${components.vnTextfield}[name="name"]`, nameInput: `${components.vnTextfield}[name="name"]`,
contactInput: `${components.vnTextfield}[name="contact"]`, contactInput: `${components.vnTextfield}[name="contact"]`,
phoneInput: `${components.vnTextfield}[name="phone"]`, phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`, mobileInput: `${components.vnTextfield}[name="mobile"]`,
faxInput: `${components.vnTextfield}[name="fax"]`, faxInput: `${components.vnTextfield}[name="fax"]`,
emailInput: `${components.vnTextfield}[name="email"]`, emailInput: `${components.vnTextfield}[name="email"]`,
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`, salesPersonAutocomplete: 'vn-autocomplete[field="$ctrl.client.salesPersonFk"]',
channelAutocomplete: `vn-autocomplete[field="$ctrl.client.contactChannelFk"]`, channelAutocomplete: 'vn-autocomplete[field="$ctrl.client.contactChannelFk"]',
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
clientFiscalData: { clientFiscalData: {
fiscalDataButton: `vn-left-menu a[ui-sref="client.card.fiscalData"]`, fiscalDataButton: 'vn-left-menu a[ui-sref="client.card.fiscalData"]',
socialNameInput: `${components.vnTextfield}[name="socialName"]`, socialNameInput: `${components.vnTextfield}[name="socialName"]`,
fiscalIdInput: `${components.vnTextfield}[name="fi"]`, fiscalIdInput: `${components.vnTextfield}[name="fi"]`,
equalizationTaxCheckbox: `vn-check[label='Is equalizated'] md-checkbox`, equalizationTaxCheckbox: 'vn-check[label="Is equalizated"] md-checkbox',
acceptPropagationButton: `vn-client-fiscal-data > vn-confirm button[response=ACCEPT]`, acceptPropagationButton: 'vn-client-fiscal-data > vn-confirm button[response=ACCEPT]',
addressInput: `${components.vnTextfield}[name="street"]`, addressInput: `${components.vnTextfield}[name="street"]`,
cityInput: `${components.vnTextfield}[name="city"]`, cityInput: `${components.vnTextfield}[name="city"]`,
postcodeInput: `${components.vnTextfield}[name="postcode"]`, postcodeInput: `${components.vnTextfield}[name="postcode"]`,
provinceAutocomplete: `vn-autocomplete[field="$ctrl.client.provinceFk"]`, provinceAutocomplete: 'vn-autocomplete[field="$ctrl.client.provinceFk"]',
countryAutocomplete: `vn-autocomplete[field="$ctrl.client.countryFk"]`, countryAutocomplete: 'vn-autocomplete[field="$ctrl.client.countryFk"]',
activeCheckbox: `vn-check[label="Active"] md-checkbox`, activeCheckbox: 'vn-check[label="Active"] md-checkbox',
frozenCheckbox: `vn-check[label="Frozen"] md-checkbox`, frozenCheckbox: 'vn-check[label="Frozen"] md-checkbox',
invoiceByAddressCheckbox: `vn-check[label='Invoice by address'] md-checkbox`, invoiceByAddressCheckbox: 'vn-check[label="Invoice by address"] md-checkbox',
verifiedDataCheckbox: `vn-check[label="Verified data"] md-checkbox`, verifiedDataCheckbox: 'vn-check[label="Verified data"] md-checkbox',
hasToInvoiceCheckbox: `vn-check[label='Has to invoice'] md-checkbox`, hasToInvoiceCheckbox: 'vn-check[label="Has to invoice"] md-checkbox',
invoiceByMailCheckbox: `vn-check[label='Invoice by mail'] md-checkbox`, invoiceByMailCheckbox: 'vn-check[label="Invoice by mail"] md-checkbox',
viesCheckbox: `vn-check[label='Vies'] md-checkbox`, viesCheckbox: 'vn-check[label="Vies"] md-checkbox',
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
clientPayMethod: { clientBillingData: {
payMethodAutocomplete: `vn-autocomplete[field="$ctrl.client.payMethodFk"]`, payMethodAutocomplete: 'vn-autocomplete[field="$ctrl.client.payMethodFk"]',
IBANInput: `${components.vnTextfield}[name="iban"]`, IBANInput: `${components.vnTextfield}[name="iban"]`,
dueDayInput: `${components.vnInputNumber}[name="dueDay"]`, dueDayInput: `${components.vnInputNumber}[name="dueDay"]`,
receivedCoreLCRCheckbox: `vn-check[label='Received LCR'] md-checkbox`, receivedCoreLCRCheckbox: 'vn-check[label="Received LCR"] md-checkbox',
receivedCoreVNLCheckbox: `vn-check[label='Received core VNL'] md-checkbox`, receivedCoreVNLCheckbox: 'vn-check[label="Received core VNL"] md-checkbox',
receivedB2BVNLCheckbox: `vn-check[label='Received B2B VNL'] md-checkbox`, receivedB2BVNLCheckbox: 'vn-check[label="Received B2B VNL"] md-checkbox',
swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"]', swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"]',
clearswiftBicButton: `vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"] > div > div > div > vn-icon > i`, clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"] > div > div > div > vn-icon > i',
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button', newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input', newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input',
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input', newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
@ -92,41 +92,41 @@ export default {
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
clientAddresses: { clientAddresses: {
addressesButton: `vn-left-menu a[ui-sref="client.card.address.index"]`, addressesButton: 'vn-left-menu a[ui-sref="client.card.address.index"]',
createAddress: `vn-client-address-index ${components.vnFloatButton}`, createAddress: `vn-client-address-index ${components.vnFloatButton}`,
defaultCheckboxInput: `vn-check[label='Default'] md-checkbox`, defaultCheckboxInput: 'vn-check[label="Default"] md-checkbox',
consigneeInput: `${components.vnTextfield}[name="nickname"]`, consigneeInput: `${components.vnTextfield}[name="nickname"]`,
streetAddressInput: `${components.vnTextfield}[name="street"]`, streetAddressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postalCode"]`, postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
cityInput: `${components.vnTextfield}[name="city"]`, cityInput: `${components.vnTextfield}[name="city"]`,
provinceAutocomplete: `vn-autocomplete[field="$ctrl.address.provinceFk"]`, provinceAutocomplete: 'vn-autocomplete[field="$ctrl.address.provinceFk"]',
agencyAutocomplete: `vn-autocomplete[field="$ctrl.address.agencyModeFk"]`, agencyAutocomplete: 'vn-autocomplete[field="$ctrl.address.agencyModeFk"]',
phoneInput: `${components.vnTextfield}[name="phone"]`, phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`, mobileInput: `${components.vnTextfield}[name="mobile"]`,
defaultAddress: 'vn-client-address-index vn-horizontal:nth-child(1) div[name="street"]', defaultAddress: 'vn-client-address-index vn-horizontal:nth-child(1) div[name="street"]',
secondMakeDefaultStar: 'vn-client-address-index vn-card vn-horizontal:nth-child(2) vn-icon-button[icon="star_border"]', secondMakeDefaultStar: 'vn-client-address-index vn-card vn-horizontal:nth-child(2) vn-icon-button[icon="star_border"]',
firstEditButton: `vn-client-address-index vn-icon-button[icon='edit']`, firstEditButton: 'vn-client-address-index vn-icon-button[icon="edit"]',
secondEditButton: `vn-client-address-index vn-horizontal:nth-child(2) vn-icon-button[icon='edit']`, secondEditButton: 'vn-client-address-index vn-horizontal:nth-child(2) vn-icon-button[icon="edit"]',
activeCheckbox: `vn-check[label='Enabled'] md-checkbox`, activeCheckbox: 'vn-check[label="Enabled"] md-checkbox',
equalizationTaxCheckbox: `vn-client-address-edit vn-check[label="Is equalizated"] md-checkbox`, equalizationTaxCheckbox: 'vn-client-address-edit vn-check[label="Is equalizated"] md-checkbox',
firstObservationTypeAutocomplete: `vn-client-address-edit [name=observations] :nth-child(1) [field="observation.observationTypeFk"]`, firstObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(1) [field="observation.observationTypeFk"]',
firstObservationDescriptionInput: `vn-client-address-edit [name=observations] :nth-child(1) [model="observation.description"] input`, firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [model="observation.description"] input',
secondObservationTypeAutocomplete: `vn-client-address-edit [name=observations] :nth-child(2) [field="observation.observationTypeFk"]`, secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [field="observation.observationTypeFk"]',
secondObservationDescriptionInput: `vn-client-address-edit [name=observations] :nth-child(2) [model="observation.description"] input`, secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [model="observation.description"] input',
addObservationButton: `vn-client-address-edit vn-icon-button[icon="add_circle"]`, addObservationButton: 'vn-client-address-edit vn-icon-button[icon="add_circle"]',
saveButton: `${components.vnSubmit}`, saveButton: `${components.vnSubmit}`,
cancelCreateAddressButton: `button[ui-sref="client.card.address.index"]`, cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button' cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button'
}, },
clientWebAccess: { clientWebAccess: {
webAccessButton: `vn-left-menu a[ui-sref="client.card.webAccess"]`, webAccessButton: 'vn-left-menu a[ui-sref="client.card.webAccess"]',
enableWebAccessCheckbox: `vn-check[label='Enable web access'] md-checkbox`, enableWebAccessCheckbox: 'vn-check[label="Enable web access"] md-checkbox',
userNameInput: `${components.vnTextfield}[name="name"]`, userNameInput: `${components.vnTextfield}[name="name"]`,
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
clientNotes: { clientNotes: {
addNoteFloatButton: `${components.vnFloatButton}`, addNoteFloatButton: `${components.vnFloatButton}`,
noteInput: `vn-textarea[label="Note"]`, noteInput: 'vn-textarea[label="Note"]',
saveButton: `${components.vnSubmit}`, saveButton: `${components.vnSubmit}`,
firstNoteText: 'vn-client-note .text' firstNoteText: 'vn-client-note .text'
}, },
@ -140,7 +140,7 @@ export default {
addGreugeFloatButton: `${components.vnFloatButton}`, addGreugeFloatButton: `${components.vnFloatButton}`,
amountInput: `${components.vnInputNumber}[name="amount"]`, amountInput: `${components.vnInputNumber}[name="amount"]`,
descriptionInput: `${components.vnTextfield}[name="description"]`, descriptionInput: `${components.vnTextfield}[name="description"]`,
typeAutocomplete: `vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]`, typeAutocomplete: 'vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]',
saveButton: `${components.vnSubmit}`, saveButton: `${components.vnSubmit}`,
firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr' firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr'
}, },
@ -151,20 +151,20 @@ export default {
firstInvoiceText: 'vn-client-invoice vn-card > div vn-table vn-tbody > vn-tr' firstInvoiceText: 'vn-client-invoice vn-card > div vn-table vn-tbody > vn-tr'
}, },
clientLog: { clientLog: {
logButton: `vn-left-menu a[ui-sref="client.card.log"]`, logButton: 'vn-left-menu a[ui-sref="client.card.log"]',
lastModificationDate: 'vn-client-log > vn-log vn-table vn-tbody > vn-tr > vn-td:nth-child(1)', lastModificationDate: 'vn-client-log > vn-log vn-table vn-tbody > vn-tr > vn-td:nth-child(1)',
lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before', lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before',
lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after' lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after'
}, },
clientRisk: { clientBalance: {
riskButton: `vn-left-menu a[ui-sref="client.card.risk.index"]`, balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
companyAutocomplete: 'vn-client-risk-index vn-autocomplete[field="$ctrl.companyFk"]', companyAutocomplete: 'vn-client-balance-index vn-autocomplete[field="$ctrl.companyFk"]',
newPaymentButton: `${components.vnFloatButton}`, newPaymentButton: `${components.vnFloatButton}`,
newPaymentBankInut: `vn-client-risk-create vn-textfield[field="$ctrl.receipt.bankFk"] input`, newPaymentBankInut: 'vn-client-balance-create vn-textfield[field="$ctrl.receipt.bankFk"] input',
newPaymentAmountInput: `vn-client-risk-create vn-input-number[field="$ctrl.receipt.amountPaid"] input`, newPaymentAmountInput: 'vn-client-balance-create vn-input-number[field="$ctrl.receipt.amountPaid"] input',
saveButton: `vn-client-risk-create vn-button[label="Save"]`, saveButton: 'vn-client-balance-create vn-button[label="Save"]',
firstRiskLineBalance: 'vn-client-risk-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)' firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
}, },
webPayment: { webPayment: {
@ -172,15 +172,15 @@ export default {
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"][aria-hidden="false"]' firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"][aria-hidden="false"]'
}, },
itemsIndex: { itemsIndex: {
searchIcon: `vn-item-index vn-searchbar vn-icon[icon="search"]`, searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]',
goBackToModuleIndexButton: `vn-item-descriptor a[href="#!/item/index"]`, goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]',
createItemButton: `${components.vnFloatButton}`, createItemButton: `${components.vnFloatButton}`,
searchResult: `vn-item-index a.vn-tr`, searchResult: 'vn-item-index a.vn-tr',
searchResultPreviewButton: `vn-item-index .buttons > [icon="desktop_windows"]`, searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]',
searchResultCloneButton: `vn-item-index .buttons > [icon="icon-clone"]`, searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
acceptClonationAlertButton: `vn-item-index [vn-id="clone"] [response="ACCEPT"]`, acceptClonationAlertButton: 'vn-item-index [vn-id="clone"] [response="ACCEPT"]',
searchItemInput: `vn-searchbar vn-textfield input`, searchItemInput: 'vn-searchbar vn-textfield input',
searchButton: `vn-searchbar vn-icon[icon="search"]`, searchButton: 'vn-searchbar vn-icon[icon="search"]',
closeItemSummaryPreview: 'vn-item-index [vn-id="preview"] button.close', closeItemSummaryPreview: 'vn-item-index [vn-id="preview"] button.close',
fieldsToShowButton: 'vn-item-index vn-table > div.ng-scope > div > vn-icon-button[icon="menu"]', fieldsToShowButton: 'vn-item-index vn-table > div.ng-scope > div > vn-icon-button[icon="menu"]',
fieldsToShowForm: 'vn-item-index > div > vn-card > div > vn-table > div.ng-scope > div > vn-dialog > div > form', fieldsToShowForm: 'vn-item-index > div > vn-card > div > vn-table > div.ng-scope > div > vn-dialog > div > form',
@ -201,90 +201,90 @@ export default {
}, },
itemCreateView: { itemCreateView: {
temporalName: `${components.vnTextfield}[name="provisionalName"]`, temporalName: `${components.vnTextfield}[name="provisionalName"]`,
typeAutocomplete: `vn-autocomplete[field="$ctrl.item.typeFk"]`, typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
intrastatAutocomplete: `vn-autocomplete[field="$ctrl.item.intrastatFk"]`, intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
originAutocomplete: `vn-autocomplete[field="$ctrl.item.originFk"]`, originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
createButton: `${components.vnSubmit}`, createButton: `${components.vnSubmit}`,
cancelButton: `button[ui-sref="item.index"]` cancelButton: 'button[ui-sref="item.index"]'
}, },
itemDescriptor: { itemDescriptor: {
moreMenu: `vn-item-descriptor vn-icon-menu > div > vn-icon`, moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon',
moreMenuRegularizeButton: `vn-item-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(1)`, moreMenuRegularizeButton: 'vn-item-descriptor vn-drop-down > vn-popover ul > li[name="Regularize stock"]',
regularizeQuantityInput: `vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input`, regularizeQuantityInput: 'vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input',
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]', regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]',
editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]', editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]',
regularizeSaveButton: `vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button`, regularizeSaveButton: 'vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button',
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]', inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]',
navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]' navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]'
}, },
itemBasicData: { itemBasicData: {
basicDataButton: `vn-left-menu a[ui-sref="item.card.data"]`, basicDataButton: 'vn-left-menu a[ui-sref="item.card.basicData"]',
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]', goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
typeAutocomplete: `vn-autocomplete[field="$ctrl.item.typeFk"]`, typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
intrastatAutocomplete: `vn-autocomplete[field="$ctrl.item.intrastatFk"]`, intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
nameInput: `vn-textfield[label="Name"] input`, nameInput: 'vn-textfield[label="Name"] input',
relevancyInput: `vn-input-number[label="Relevancy"] input`, relevancyInput: 'vn-input-number[label="Relevancy"] input',
originAutocomplete: `vn-autocomplete[field="$ctrl.item.originFk"]`, originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
expenceAutocomplete: `vn-autocomplete[field="$ctrl.item.expenceFk"]`, expenceAutocomplete: 'vn-autocomplete[field="$ctrl.item.expenceFk"]',
longNameInput: `vn-textfield[field="$ctrl.item.longName"] input`, longNameInput: 'vn-textfield[field="$ctrl.item.longName"] input',
isActiveCheckbox: `vn-check[label='Active'] md-checkbox`, isActiveCheckbox: 'vn-check[label="Active"] md-checkbox',
submitBasicDataButton: `${components.vnSubmit}` submitBasicDataButton: `${components.vnSubmit}`
}, },
itemTags: { itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]', goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
tagsButton: `vn-left-menu a[ui-sref="item.card.tags"]`, tagsButton: 'vn-left-menu a[ui-sref="item.card.tags"]',
fourthTagAutocomplete: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"]`, fourthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"]',
fourthValueInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input`, fourthValueInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input',
fourthRelevancyInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input`, fourthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input',
fourthRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]`, fourthRemoveTagButton: 'vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]',
fifthTagAutocomplete: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"]`, fifthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"]',
fifthValueInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input`, fifthValueInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input',
fifthRelevancyInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input`, fifthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input',
sixthTagAutocomplete: `vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"]`, sixthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"]',
sixthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input`, sixthValueInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input',
sixthRelevancyInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input`, sixthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input',
seventhTagAutocomplete: `vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[field="itemTag.tagFk"]`, seventhTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[field="itemTag.tagFk"]',
seventhValueInput: `vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Value"] input`, seventhValueInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Value"] input',
seventhRelevancyInput: `vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input`, seventhRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input',
addItemTagButton: `vn-item-tags vn-icon-button[icon="add_circle"]`, addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}` submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
}, },
itemTax: { itemTax: {
firstClassAutocomplete: `vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[field="tax.taxClassFk"]`, firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[field="tax.taxClassFk"]',
secondClassAutocomplete: `vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[field="tax.taxClassFk"]`, secondClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[field="tax.taxClassFk"]',
thirdClassAutocomplete: `vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[field="tax.taxClassFk"]`, thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[field="tax.taxClassFk"]',
submitTaxButton: `vn-item-tax ${components.vnSubmit}` submitTaxButton: `vn-item-tax ${components.vnSubmit}`
}, },
itemBarcodes: { itemBarcodes: {
addBarcodeButton: `vn-item-barcode vn-icon[icon="add_circle"]`, addBarcodeButton: 'vn-item-barcode vn-icon[icon="add_circle"]',
thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > ${components.vnTextfield}`, thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > ${components.vnTextfield}`,
submitBarcodesButton: `vn-item-barcode ${components.vnSubmit}`, submitBarcodesButton: `vn-item-barcode ${components.vnSubmit}`,
firstCodeRemoveButton: `vn-item-barcode vn-horizontal vn-none vn-icon[icon="delete"]` firstCodeRemoveButton: 'vn-item-barcode vn-horizontal vn-none vn-icon[icon="delete"]'
}, },
itemNiches: { itemNiches: {
addNicheButton: `vn-item-niche vn-icon[icon="add_circle"]`, addNicheButton: 'vn-item-niche vn-icon[icon="add_circle"]',
firstWarehouseAutocomplete: `vn-item-niche vn-autocomplete[field="niche.warehouseFk"]`, firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[field="niche.warehouseFk"]',
firstCodeInput: `vn-item-niche vn-horizontal:nth-child(1) > vn-textfield[label="Code"] input`, firstCodeInput: 'vn-item-niche vn-horizontal:nth-child(1) > vn-textfield[label="Code"] input',
secondWarehouseAutocomplete: `vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[field="niche.warehouseFk"]`, secondWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[field="niche.warehouseFk"]',
secondCodeInput: `vn-item-niche vn-horizontal:nth-child(2) > vn-textfield[label="Code"] input`, secondCodeInput: 'vn-item-niche vn-horizontal:nth-child(2) > vn-textfield[label="Code"] input',
secondNicheRemoveButton: `vn-item-niche vn-horizontal:nth-child(2) > vn-none > vn-icon-button[icon="delete"]`, secondNicheRemoveButton: 'vn-item-niche vn-horizontal:nth-child(2) > vn-none > vn-icon-button[icon="delete"]',
thirdWarehouseAutocomplete: `vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[field="niche.warehouseFk"]`, thirdWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[field="niche.warehouseFk"]',
thirdCodeInput: `vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input`, thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input',
submitNichesButton: `vn-item-niche ${components.vnSubmit}` submitNichesButton: `vn-item-niche ${components.vnSubmit}`
}, },
itemBotanical: { itemBotanical: {
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`, botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
genusAutocomplete: `vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]`, genusAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]',
speciesAutocomplete: `vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]`, speciesAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]',
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}` submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
}, },
itemSummary: { itemSummary: {
basicData: `vn-item-summary [name="basicData"]`, basicData: 'vn-item-summary [name="basicData"]',
vat: `vn-item-summary [name="tax"]`, vat: 'vn-item-summary [name="tax"]',
tags: `vn-item-summary [name="tags"]`, tags: 'vn-item-summary [name="tags"]',
niche: `vn-item-summary [name="niche"]`, niche: 'vn-item-summary [name="niche"]',
botanical: `vn-item-summary [name="botanical"]`, botanical: 'vn-item-summary [name="botanical"]',
barcode: `vn-item-summary [name="barcode"]` barcode: 'vn-item-summary [name="barcode"]'
}, },
itemDiary: { itemDiary: {
thirdTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(3) > vn-td:nth-child(2) > span', thirdTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(3) > vn-td:nth-child(2) > span',
@ -301,20 +301,20 @@ export default {
firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span',
popoverDiaryButton: 'vn-ticket-summary vn-item-descriptor-popover vn-item-descriptor vn-icon[icon="icon-transaction"]', popoverDiaryButton: 'vn-ticket-summary vn-item-descriptor-popover vn-item-descriptor vn-icon[icon="icon-transaction"]',
firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3)', firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3)',
firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6)' firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6)',
invoiceOutRef: 'vn-ticket-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(6) > section > span'
}, },
ticketsIndex: { ticketsIndex: {
newTicketButton: 'vn-ticket-index > a', newTicketButton: 'vn-ticket-index > a',
searchResult: `vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr`, searchResult: 'vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
searchResultDate: `vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)`, searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
searchResultAddress: `vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)`,
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`, searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
searchButton: `vn-ticket-index vn-searchbar vn-icon[icon="search"]`, searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]',
moreMenu: `vn-ticket-index vn-icon-menu[vn-id="more-button"] > div > vn-icon`, moreMenu: 'vn-ticket-index vn-icon-menu[vn-id="more-button"] > div > vn-icon',
moreMenuTurns: `vn-ticket-index vn-icon-menu vn-drop-down > vn-popover li`, moreMenuTurns: 'vn-ticket-index vn-icon-menu vn-drop-down > vn-popover li:nth-child(2)',
sixthWeeklyTicketTurn: `vn-ticket-weekly > form > div > vn-card > div > vn-table > div > vn-tbody > vn-tr:nth-child(6) > vn-td:nth-child(3) > vn-autocomplete > div > div > input`, sixthWeeklyTicketTurn: 'vn-ticket-weekly > form > div > vn-card > div > vn-table > div > vn-tbody > vn-tr:nth-child(6) > vn-td:nth-child(3) > vn-autocomplete > div > div > input',
weeklyTicket: `vn-ticket-weekly vn-table > div > vn-tbody > vn-tr`, weeklyTicket: 'vn-ticket-weekly vn-table > div > vn-tbody > vn-tr',
sixthWeeklyTicketDeleteIcon: `vn-ticket-weekly > form vn-tbody > vn-tr:nth-child(6) > vn-td:nth-child(6) > vn-icon-button[icon="delete"]` sixthWeeklyTicketDeleteIcon: 'vn-ticket-weekly > form vn-tbody > vn-tr:nth-child(6) > vn-td:nth-child(6) > vn-icon-button[icon="delete"]'
}, },
createTicketView: { createTicketView: {
clientAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.clientFk"]', clientAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.clientFk"]',
@ -325,57 +325,59 @@ export default {
createButton: `${components.vnSubmit}` createButton: `${components.vnSubmit}`
}, },
ticketDescriptor: { ticketDescriptor: {
moreMenu: `vn-ticket-descriptor vn-icon-menu > div > vn-icon`, moreMenu: 'vn-ticket-descriptor vn-icon-menu > div > vn-icon',
moreMenuAddStowaway: `vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(1)`, moreMenuAddStowaway: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Add stowaway"]',
moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(5)', moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Remove stowaway"]',
moreMenuAddToTurn: `vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(2)`, moreMenuAddToTurn: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Add turn"]',
moreMenuDeleteTicket: `vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(3)`, moreMenuDeleteTicket: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Delete ticket"]',
moreMenuMakeInvoice: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Make invoice"]',
addStowawayDialogSecondTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tr:nth-child(2)', addStowawayDialogSecondTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tr:nth-child(2)',
shipSelectButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks > vn-button-menu[icon="icon-stowaway"]', shipSelectButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks > vn-button-menu[icon="icon-stowaway"]',
shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]', shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]',
shipMenuSecondTicket: 'vn-ticket-descriptor div.quicklinks vn-drop-down li:nth-child(2)', shipMenuSecondTicket: 'vn-ticket-descriptor div.quicklinks vn-drop-down li:nth-child(2)',
thursdayButton: `vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(4)`, thursdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(4)',
saturdayButton: `vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)`, saturdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)',
closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]', closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]',
acceptDeleteButton: 'vn-ticket-descriptor button[response="ACCEPT"]', acceptDeleteButton: 'vn-ticket-descriptor button[response="ACCEPT"]',
acceptInvoiceOutButton: 'vn-ticket-descriptor vn-confirm[vn-id="makeInvoiceConfirmation"] button[response="ACCEPT"]',
acceptDeleteStowawayButton: 'vn-ticket-descriptor > vn-remove-stowaway button[response="ACCEPT"]' acceptDeleteStowawayButton: 'vn-ticket-descriptor > vn-remove-stowaway button[response="ACCEPT"]'
}, },
ticketNotes: { ticketNotes: {
firstNoteRemoveButton: `vn-icon[icon="delete"]`, firstNoteRemoveButton: 'vn-icon[icon="delete"]',
addNoteButton: `vn-icon[icon="add_circle"]`, addNoteButton: 'vn-icon[icon="add_circle"]',
firstNoteTypeAutocomplete: `vn-autocomplete[field="observation.observationTypeFk"]`, firstNoteTypeAutocomplete: 'vn-autocomplete[field="observation.observationTypeFk"]',
firstDescriptionInput: `vn-textfield[label="Description"] input`, firstDescriptionInput: 'vn-textfield[label="Description"] input',
submitNotesButton: `${components.vnSubmit}` submitNotesButton: `${components.vnSubmit}`
}, },
ticketExpedition: { ticketExpedition: {
expeditionButton: `vn-left-menu a[ui-sref="ticket.card.expedition"]`, expeditionButton: 'vn-left-menu a[ui-sref="ticket.card.expedition"]',
secondExpeditionRemoveButton: `vn-ticket-expedition vn-table div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button[icon="delete"]`, secondExpeditionRemoveButton: 'vn-ticket-expedition vn-table div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button[icon="delete"]',
acceptDeleteRowButton: `vn-ticket-expedition > vn-confirm[vn-id="delete-expedition"] button[response=ACCEPT]`, acceptDeleteRowButton: 'vn-ticket-expedition > vn-confirm[vn-id="delete-expedition"] button[response=ACCEPT]',
expeditionRow: `vn-ticket-expedition vn-table vn-tbody > vn-tr` expeditionRow: 'vn-ticket-expedition vn-table vn-tbody > vn-tr'
}, },
ticketPackages: { ticketPackages: {
packagesButton: `vn-left-menu a[ui-sref="ticket.card.package.index"]`, packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
firstPackageAutocomplete: `vn-autocomplete[label="Package"]`, firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
firstQuantityInput: `vn-input-number[label="Quantity"] input`, firstQuantityInput: 'vn-input-number[label="Quantity"] input',
firstRemovePackageButton: `vn-icon-button[vn-tooltip="Remove package"]`, firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
addPackageButton: `vn-icon-button[vn-tooltip="Add package"]`, addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
clearPackageAutocompleteButton: `vn-autocomplete[label="Package"] > div > div > div > vn-icon > i`, clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] > div > div > div > vn-icon > i',
savePackagesButton: `${components.vnSubmit}` savePackagesButton: `${components.vnSubmit}`
}, },
ticketSales: { ticketSales: {
saleButton: `vn-left-menu a[ui-sref="ticket.card.sale"]`, saleButton: 'vn-left-menu a[ui-sref="ticket.card.sale"]',
saleLine: `vn-table div > vn-tbody > vn-tr`, saleLine: 'vn-table div > vn-tbody > vn-tr',
saleDescriptorPopover: 'vn-ticket-sale vn-item-descriptor-popover > vn-popover', saleDescriptorPopover: 'vn-ticket-sale vn-item-descriptor-popover > vn-popover',
saleDescriptorPopoverSummaryButton: 'vn-item-descriptor-popover a[href="#!/item/1/summary"]', saleDescriptorPopoverSummaryButton: 'vn-item-descriptor-popover a[href="#!/item/1/summary"]',
descriptorItemDiaryButton: `vn-item-descriptor .quicklinks.ng-scope > vn-horizontal > a > vn-icon > i`, descriptorItemDiaryButton: 'vn-item-descriptor .quicklinks.ng-scope > vn-horizontal > a > vn-icon > i',
newItemButton: 'vn-float-button[icon="add"]', newItemButton: 'vn-float-button[icon="add"]',
firstSaleDescriptorImage: 'vn-ticket-sale vn-item-descriptor-popover > vn-popover vn-item-descriptor img', firstSaleDescriptorImage: 'vn-ticket-sale vn-item-descriptor-popover > vn-popover vn-item-descriptor img',
firstSaleText: `vn-table div > vn-tbody > vn-tr:nth-child(1)`, firstSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(1)',
firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img', firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img',
firstSaleZoomedImage: 'body > div > div > img', firstSaleZoomedImage: 'body > div > div > img',
firstSaleQuantity: `vn-textfield[model="sale.quantity"]:nth-child(1) input`, firstSaleQuantity: 'vn-textfield[model="sale.quantity"]:nth-child(1) input',
firstSaleQuantityCell: `vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable`, firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable',
firstSaleQuantityClearInput: `vn-textfield[model="sale.quantity"] div.suffix > i`, firstSaleQuantityClearInput: 'vn-textfield[model="sale.quantity"] div.suffix > i',
firstSaleID: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(4) > span', firstSaleID: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(4) > span',
firstSalePrice: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) > vn-td:nth-child(7) > span', firstSalePrice: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
firstSalePriceInput: 'vn-ticket-sale:nth-child(1) vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown vn-textfield input', firstSalePriceInput: 'vn-ticket-sale:nth-child(1) vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown vn-textfield input',
@ -383,52 +385,45 @@ export default {
firstSaleDiscountInput: 'vn-ticket-sale:nth-child(1) vn-ticket-sale-edit-discount vn-textfield input', firstSaleDiscountInput: 'vn-ticket-sale:nth-child(1) vn-ticket-sale-edit-discount vn-textfield input',
firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(9)', firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(9)',
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)', firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
firstSaleColour: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(1)`, firstSaleColour: 'vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(1)',
firstSaleLength: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(3)`, firstSaleLength: 'vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(3)',
firstSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"] md-checkbox`, firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"] md-checkbox',
secondSaleClaimIcon: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > a > vn-icon', secondSaleClaimIcon: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > a > vn-icon',
secondSaleColour: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(6) section:nth-child(5)`, secondSaleColour: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(6) section:nth-child(5)',
secondSalePrice: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span`, secondSalePrice: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span',
secondSaleDiscount: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)`, secondSaleDiscount: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)',
secondSaleImport: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(9)`, secondSaleImport: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(9)',
secondSaleText: `vn-table div > vn-tbody > vn-tr:nth-child(2)`, secondSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(2)',
totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong', totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong',
selectAllSalesCheckbox: `vn-ticket-sale vn-thead vn-check md-checkbox`, selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check md-checkbox',
secondSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(2) vn-check[field="sale.checked"] md-checkbox`, secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[field="sale.checked"] md-checkbox',
thirdSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(3) vn-check[field="sale.checked"] md-checkbox`, thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[field="sale.checked"] md-checkbox',
deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]', deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]',
transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]', transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]',
moveToTicketInput: 'vn-ticket-sale vn-popover.transfer vn-textfield[model="$ctrl.moveToTicketFk"] input', moveToTicketInput: 'vn-ticket-sale vn-popover.transfer vn-textfield[model="$ctrl.moveToTicketFk"] input',
moveToTicketInputClearButton: 'vn-popover.shown i[title="Clear"]', moveToTicketInputClearButton: 'vn-popover.shown i[title="Clear"]',
moveToTicketButton: 'vn-ticket-sale vn-popover.transfer vn-icon[icon="arrow_forward_ios"]', moveToTicketButton: 'vn-ticket-sale vn-popover.transfer vn-icon[icon="arrow_forward_ios"]',
moveToNewTicketButton: 'vn-ticket-sale vn-popover.transfer vn-button[label="New ticket"]', moveToNewTicketButton: 'vn-ticket-sale vn-popover.transfer vn-button[label="New ticket"]',
acceptDeleteLineButton: `vn-ticket-sale > vn-confirm[vn-id="delete-lines"] button[response=ACCEPT]`, acceptDeleteLineButton: 'vn-ticket-sale > vn-confirm[vn-id="delete-lines"] button[response=ACCEPT]',
acceptDeleteTicketButton: `vn-ticket-sale > vn-confirm[vn-id="deleteConfirmation"] button[response=ACCEPT]`, acceptDeleteTicketButton: 'vn-ticket-sale > vn-confirm[vn-id="deleteConfirmation"] button[response=ACCEPT]',
stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"] button', stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"]'
stateMenuOptions: 'vn-ticket-sale vn-drop-down > vn-popover ul > li:nth-child(1)',
moreMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="More"] button',
moreMenuReseveOption: 'vn-ticket-sale vn-drop-down > vn-popover ul > li:nth-child(2)',
moreMenuUnmarkResevedOption: 'vn-ticket-sale vn-drop-down > vn-popover ul > li:nth-child(3)',
moreMenuUpdateDiscount: 'vn-ticket-sale vn-drop-down > vn-popover ul > li:nth-child(4)',
moreMenuUpdateDiscountInput: 'vn-ticket-sale vn-dialog.shown vn-ticket-sale-edit-discount input',
moreMenuCreateClaim: 'vn-ticket-sale vn-drop-down > vn-popover ul > li:nth-child(1)'
}, },
ticketTracking: { ticketTracking: {
trackingButton: `vn-left-menu a[ui-sref="ticket.card.tracking.index"]`, trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]',
createStateButton: `${components.vnFloatButton}`, createStateButton: `${components.vnFloatButton}`,
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.stateFk"]', stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.stateFk"]',
saveButton: `${components.vnSubmit}`, saveButton: `${components.vnSubmit}`,
cancelButton: `vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]` cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]'
}, },
ticketBasicData: { ticketBasicData: {
basicDataButton: `vn-left-menu a[ui-sref="ticket.card.data.stepOne"]`, basicDataButton: 'vn-left-menu a[ui-sref="ticket.card.basicData.stepOne"]',
clientAutocomplete: `vn-autocomplete[field="$ctrl.clientFk"]`, clientAutocomplete: 'vn-autocomplete[field="$ctrl.clientFk"]',
addressAutocomplete: `vn-autocomplete[field="$ctrl.ticket.addressFk"]`, addressAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.addressFk"]',
agencyAutocomplete: `vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]`, agencyAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]',
nextStepButton: `vn-step-control > section > section.buttons > section:nth-child(2) > vn-button`, nextStepButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-button',
finalizeButton: `vn-step-control > section > section.buttons > section:nth-child(2) > vn-submit`, finalizeButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-submit',
stepTwoTotalPriceDif: `vn-ticket-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)`, stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)',
chargesReasonAutocomplete: `vn-autocomplete[field="$ctrl.ticket.option"]`, chargesReasonAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.option"]',
}, },
ticketComponents: { ticketComponents: {
base: 'vn-ticket-components tfoot > tr:nth-child(1) > td', base: 'vn-ticket-components tfoot > tr:nth-child(1) > td',
@ -436,19 +431,19 @@ export default {
total: 'vn-ticket-components tfoot > tr:nth-child(3) > td' total: 'vn-ticket-components tfoot > tr:nth-child(3) > td'
}, },
ticketRequests: { ticketRequests: {
addRequestButton: `vn-ticket-request-index > a > vn-float-button > button`, addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button',
request: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr', request: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr',
descriptionInput: `vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield > div > div > div.infix > input`, descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield > div > div > div.infix > input',
atenderAutocomplete: `vn-ticket-request-create vn-autocomplete[field="$ctrl.ticketRequest.atenderFk"]`, atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[field="$ctrl.ticketRequest.atenderFk"]',
quantityInput: `vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(1) > div > div > div.infix > input`, quantityInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(1) > div > div > div.infix > input',
priceInput: `vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(2) > div > div > div.infix > input`, priceInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(2) > div > div > div.infix > input',
firstRemoveRequestButton: `vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)`, firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
saveButton: `vn-ticket-request-create > form > div > vn-button-bar > vn-submit[label="Create"] input`, saveButton: 'vn-ticket-request-create > form > div > vn-button-bar > vn-submit[label="Create"] input',
firstDescription: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2)', firstDescription: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2)',
}, },
ticketLog: { ticketLog: {
logButton: `vn-left-menu a[ui-sref="ticket.card.log"]`, logButton: 'vn-left-menu a[ui-sref="ticket.card.log"]',
changedBy: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2) > span', changedBy: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2) > span',
actionTaken: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > div > div:nth-child(3) > span.value.ng-scope.ng-binding', actionTaken: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > div > div:nth-child(3) > span.value.ng-scope.ng-binding',
id: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr > vn-td.before > vn-one:nth-child(1) > div > span.value.ng-scope.ng-binding' id: 'vn-ticket-log > vn-log > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr > vn-td.before > vn-one:nth-child(1) > div > span.value.ng-scope.ng-binding'
@ -464,25 +459,25 @@ export default {
saveServiceButton: `${components.vnSubmit}` saveServiceButton: `${components.vnSubmit}`
}, },
createStateView: { createStateView: {
stateAutocomplete: `vn-autocomplete[field="$ctrl.stateFk"]`, stateAutocomplete: 'vn-autocomplete[field="$ctrl.stateFk"]',
workerAutocomplete: `vn-autocomplete[field="$ctrl.workerFk"]`, workerAutocomplete: 'vn-autocomplete[field="$ctrl.workerFk"]',
clearStateInputButton: `vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i`, clearStateInputButton: 'vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i',
saveStateButton: `${components.vnSubmit}` saveStateButton: `${components.vnSubmit}`
}, },
claimsIndex: { claimsIndex: {
searchClaimInput: `vn-claim-index ${components.vnTextfield}`, searchClaimInput: `vn-claim-index ${components.vnTextfield}`,
searchResult: `vn-claim-index vn-card > div > vn-table > div > vn-tbody > a`, searchResult: 'vn-claim-index vn-card > div > vn-table > div > vn-tbody > a',
searchButton: `vn-claim-index vn-searchbar vn-icon[icon="search"]` searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]'
}, },
claimBasicData: { claimBasicData: {
claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[field="$ctrl.claim.claimStateFk"]', claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[field="$ctrl.claim.claimStateFk"]',
isPaidWithManaCheckbox: 'vn-check[field="$ctrl.claim.isChargedToMana"] md-checkbox', isPaidWithManaCheckbox: 'vn-check[field="$ctrl.claim.isChargedToMana"] md-checkbox',
responsabilityInputRange: `vn-input-range`, responsabilityInputRange: 'vn-input-range',
observationInput: `vn-textarea[label="Observation"] textarea`, observationInput: 'vn-textarea[label="Observation"] textarea',
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
claimDetail: { claimDetail: {
addItemButton: `vn-claim-detail a vn-float-button`, addItemButton: 'vn-claim-detail a vn-float-button',
firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr', firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr',
claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr', claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr',
secondItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(2) vn-input-number[model="saleClaimed.quantity"] input', secondItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(2) vn-input-number[model="saleClaimed.quantity"] input',
@ -516,29 +511,29 @@ export default {
}, },
ordersIndex: { ordersIndex: {
searchResult: `vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr`, searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
searchResultDate: `vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)`, searchResultDate: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)',
searchResultAddress: `vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)`, searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)',
searchOrderInput: `vn-order-index ${components.vnTextfield}`, searchOrderInput: `vn-order-index ${components.vnTextfield}`,
searchButton: `vn-order-index vn-searchbar vn-icon[icon="search"]`, searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]',
createOrderButton: `${components.vnFloatButton}`, createOrderButton: `${components.vnFloatButton}`,
}, },
createOrderView: { createOrderView: {
clientAutocomplete: `vn-autocomplete[label="Client"]`, clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: `vn-autocomplete[label="Address"]`, addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: `vn-autocomplete[label="Agency"]`, agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
landedDatePicker: `vn-date-picker[label="Landed"]`, landedDatePicker: 'vn-date-picker[label="Landed"]',
createButton: `${components.vnSubmit}`, createButton: `${components.vnSubmit}`,
cancelButton: `vn-button[href="#!/client/index"]` cancelButton: 'vn-button[href="#!/client/index"]'
}, },
orderCatalog: { orderCatalog: {
orderByAutocomplete: `vn-autocomplete[label="Order by"]`, orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
}, },
orderBasicData: { orderBasicData: {
clientAutocomplete: `vn-autocomplete[label="Client"]`, clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: `vn-autocomplete[label="Address"]`, addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: `vn-autocomplete[label="Agency"]`, agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
observationInput: `vn-textarea[label="Observation"] textarea`, observationInput: 'vn-textarea[label="Observation"] textarea',
saveButton: `${components.vnSubmit}` saveButton: `${components.vnSubmit}`
}, },
orderLine: { orderLine: {

View File

@ -41,7 +41,7 @@ describe('Client create path', () => {
.write(selectors.createClientView.taxNumber, '74451390E') .write(selectors.createClientView.taxNumber, '74451390E')
.write(selectors.createClientView.userName, 'CaptainMarvel') .write(selectors.createClientView.userName, 'CaptainMarvel')
.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es') .write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es')
.autocompleteSearch(selectors.createClientView.salesPersonAutocomplete, 'Accessory') .autocompleteSearch(selectors.createClientView.salesPersonAutocomplete, 'replenisher')
.waitToClick(selectors.createClientView.createButton) .waitToClick(selectors.createClientView.createButton)
.waitForLastSnackbar(); .waitForLastSnackbar();

View File

@ -114,7 +114,7 @@ describe('Client Edit basicData path', () => {
.write(selectors.clientBasicData.mobileInput, '987654321') .write(selectors.clientBasicData.mobileInput, '987654321')
.clearInput(selectors.clientBasicData.emailInput) .clearInput(selectors.clientBasicData.emailInput)
.write(selectors.clientBasicData.emailInput, 'Storm@verdnatura.es') .write(selectors.clientBasicData.emailInput, 'Storm@verdnatura.es')
.autocompleteSearch(selectors.clientBasicData.salesPersonAutocomplete, 'AccessoryNick') .autocompleteSearch(selectors.clientBasicData.salesPersonAutocomplete, 'replenisherNick')
.autocompleteSearch(selectors.clientBasicData.channelAutocomplete, 'Metropolis newspaper') .autocompleteSearch(selectors.clientBasicData.channelAutocomplete, 'Metropolis newspaper')
.waitToClick(selectors.clientBasicData.saveButton) .waitToClick(selectors.clientBasicData.saveButton)
.waitForLastSnackbar(); .waitForLastSnackbar();
@ -162,7 +162,7 @@ describe('Client Edit basicData path', () => {
const result = await nightmare const result = await nightmare
.waitToGetProperty(`${selectors.clientBasicData.salesPersonAutocomplete} input`, 'value'); .waitToGetProperty(`${selectors.clientBasicData.salesPersonAutocomplete} input`, 'value');
expect(result).toEqual('accessoryNick'); expect(result).toEqual('replenisherNick');
}); });
it('should now confirm the channel have been selected', async() => { it('should now confirm the channel have been selected', async() => {

View File

@ -0,0 +1,121 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('Client Edit billing data path', () => {
const nightmare = createNightmare();
beforeAll(() => {
nightmare
.loginAndModule('administrative', 'client')
.accessToSearchResult('Bruce Banner')
.accessToSection('client.card.billingData');
});
it(`should attempt to edit the billing data without an IBAN but fail`, async() => {
const snackbarMessage = await nightmare
.autocompleteSearch(selectors.clientBillingData.payMethodAutocomplete, 'PayMethod with IBAN')
.autocompleteSearch(selectors.clientBillingData.swiftBicAutocomplete, 'BBKKESMMMMM')
.clearInput(selectors.clientBillingData.dueDayInput)
.write(selectors.clientBillingData.dueDayInput, '60')
.waitForTextInInput(selectors.clientBillingData.dueDayInput, '60')
.waitToClick(selectors.clientBillingData.receivedCoreLCRCheckbox)
.waitToClick(selectors.clientBillingData.receivedCoreVNLCheckbox)
.waitToClick(selectors.clientBillingData.receivedB2BVNLCheckbox)
.waitToClick(selectors.clientBillingData.saveButton)
.waitForLastSnackbar();
expect(snackbarMessage).toEqual('That payment method requires an IBAN');
});
it(`should add the IBAN but fail as it requires a BIC code`, async() => {
const snackbarMessage = await nightmare
.waitToClick(selectors.clientBillingData.clearswiftBicButton)
.clearInput(selectors.clientBillingData.IBANInput)
.write(selectors.clientBillingData.IBANInput, 'FR9121000418450200051332')
.waitForTextInInput(selectors.clientBillingData.IBANInput, 'FR9121000418450200051332')
.wait(1000)
.waitToClick(selectors.clientBillingData.saveButton)
.waitForLastSnackbar();
expect(snackbarMessage).toEqual('That payment method requires a BIC');
});
it(`should create a new BIC code`, async() => {
const newcode = await nightmare
.waitToClick(selectors.clientBillingData.newBankEntityButton)
.write(selectors.clientBillingData.newBankEntityName, 'Gotham City Bank')
.write(selectors.clientBillingData.newBankEntityCode, 9999)
.write(selectors.clientBillingData.newBankEntityBIC, 'GTHMCT')
.waitToClick(selectors.clientBillingData.acceptBankEntityButton)
.waitToGetProperty(`${selectors.clientBillingData.swiftBicAutocomplete} input`, 'value');
expect(newcode).toEqual('GTHMCT Gotham City Bank');
});
it(`should confirm the IBAN pay method is sucessfully saved`, async() => {
const payMethod = await nightmare
.waitToGetProperty(`${selectors.clientBillingData.payMethodAutocomplete} input`, 'value');
expect(payMethod).toEqual('PayMethod with IBAN');
});
it(`should clear the BIC code field, update the IBAN to see how he BIC code autocompletes`, async() => {
const AutomaticCode = await nightmare
.clearInput(selectors.clientBillingData.IBANInput)
.waitToClick(selectors.clientBillingData.clearswiftBicButton)
.write(selectors.clientBillingData.IBANInput, 'ES9121000418450200051332')
.waitToGetProperty(`${selectors.clientBillingData.swiftBicAutocomplete} input`, 'value');
expect(AutomaticCode).toEqual('CAIXESBB Caixa Bank');
});
it(`should save the form with all its new data`, async() => {
const snackbarMessages = await nightmare
.waitToClick(selectors.clientBillingData.saveButton)
.waitForSnackbar();
expect(snackbarMessages).toEqual(jasmine.arrayContaining(['Data saved!']));
});
it('should confirm the due day have been edited', async() => {
const dueDate = await nightmare
.waitToGetProperty(selectors.clientBillingData.dueDayInput, 'value');
expect(dueDate).toEqual('60');
});
it('should confirm the IBAN was saved', async() => {
const IBAN = await nightmare
.waitToGetProperty(selectors.clientBillingData.IBANInput, 'value');
expect(IBAN).toEqual('ES9121000418450200051332');
});
it('should confirm the swift / BIC code was saved', async() => {
const code = await nightmare
.waitToGetProperty(`${selectors.clientBillingData.swiftBicAutocomplete} input`, 'value');
expect(code).toEqual('CAIXESBB Caixa Bank');
});
it('should confirm Received LCR checkbox is checked', async() => {
const result = await nightmare
.checkboxState(selectors.clientBillingData.receivedCoreLCRCheckbox);
expect(result).toBe('checked');
});
it('should confirm Received core VNL checkbox is unchecked', async() => {
const result = await nightmare
.checkboxState(selectors.clientBillingData.receivedCoreVNLCheckbox);
expect(result).toBe('unchecked');
});
it('should confirm Received B2B VNL checkbox is unchecked', async() => {
const result = await nightmare
.checkboxState(selectors.clientBillingData.receivedB2BVNLCheckbox);
expect(result).toBe('unchecked');
});
});

View File

@ -1,121 +0,0 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('Client Edit pay method path', () => {
const nightmare = createNightmare();
beforeAll(() => {
nightmare
.loginAndModule('administrative', 'client')
.accessToSearchResult('Bruce Banner')
.accessToSection('client.card.billingData');
});
it(`should attempt to edit the Pay method without an IBAN but fail`, async() => {
const snackbarMessage = await nightmare
.autocompleteSearch(selectors.clientPayMethod.payMethodAutocomplete, 'PayMethod with IBAN')
.autocompleteSearch(selectors.clientPayMethod.swiftBicAutocomplete, 'BBKKESMMMMM')
.clearInput(selectors.clientPayMethod.dueDayInput)
.write(selectors.clientPayMethod.dueDayInput, '60')
.waitForTextInInput(selectors.clientPayMethod.dueDayInput, '60')
.waitToClick(selectors.clientPayMethod.receivedCoreLCRCheckbox)
.waitToClick(selectors.clientPayMethod.receivedCoreVNLCheckbox)
.waitToClick(selectors.clientPayMethod.receivedB2BVNLCheckbox)
.waitToClick(selectors.clientPayMethod.saveButton)
.waitForLastSnackbar();
expect(snackbarMessage).toEqual('That payment method requires an IBAN');
});
it(`should add the IBAN but fail as it requires a BIC code`, async() => {
const snackbarMessage = await nightmare
.waitToClick(selectors.clientPayMethod.clearswiftBicButton)
.clearInput(selectors.clientPayMethod.IBANInput)
.write(selectors.clientPayMethod.IBANInput, 'FR9121000418450200051332')
.waitForTextInInput(selectors.clientPayMethod.IBANInput, 'FR9121000418450200051332')
.wait(1000)
.waitToClick(selectors.clientPayMethod.saveButton)
.waitForLastSnackbar();
expect(snackbarMessage).toEqual('That payment method requires a BIC');
});
it(`should create a new BIC code`, async() => {
const newcode = await nightmare
.waitToClick(selectors.clientPayMethod.newBankEntityButton)
.write(selectors.clientPayMethod.newBankEntityName, 'Gotham City Bank')
.write(selectors.clientPayMethod.newBankEntityCode, 9999)
.write(selectors.clientPayMethod.newBankEntityBIC, 'GTHMCT')
.waitToClick(selectors.clientPayMethod.acceptBankEntityButton)
.waitToGetProperty(`${selectors.clientPayMethod.swiftBicAutocomplete} input`, 'value');
expect(newcode).toEqual('GTHMCT Gotham City Bank');
});
it(`should confirm the IBAN pay method is sucessfully saved`, async() => {
const payMethod = await nightmare
.waitToGetProperty(`${selectors.clientPayMethod.payMethodAutocomplete} input`, 'value');
expect(payMethod).toEqual('PayMethod with IBAN');
});
it(`should clear the BIC code field, update the IBAN to see how he BIC code autocompletes`, async() => {
const AutomaticCode = await nightmare
.clearInput(selectors.clientPayMethod.IBANInput)
.waitToClick(selectors.clientPayMethod.clearswiftBicButton)
.write(selectors.clientPayMethod.IBANInput, 'ES9121000418450200051332')
.waitToGetProperty(`${selectors.clientPayMethod.swiftBicAutocomplete} input`, 'value');
expect(AutomaticCode).toEqual('CAIXESBB Caixa Bank');
});
it(`should save the form with all its new data`, async() => {
const snackbarMessages = await nightmare
.waitToClick(selectors.clientPayMethod.saveButton)
.waitForSnackbar();
expect(snackbarMessages).toEqual(jasmine.arrayContaining(['Data saved!']));
});
it('should confirm the due day have been edited', async() => {
const dueDate = await nightmare
.waitToGetProperty(selectors.clientPayMethod.dueDayInput, 'value');
expect(dueDate).toEqual('60');
});
it('should confirm the IBAN was saved', async() => {
const IBAN = await nightmare
.waitToGetProperty(selectors.clientPayMethod.IBANInput, 'value');
expect(IBAN).toEqual('ES9121000418450200051332');
});
it('should confirm the swift / BIC code was saved', async() => {
const code = await nightmare
.waitToGetProperty(`${selectors.clientPayMethod.swiftBicAutocomplete} input`, 'value');
expect(code).toEqual('CAIXESBB Caixa Bank');
});
it('should confirm Received LCR checkbox is checked', async() => {
const result = await nightmare
.checkboxState(selectors.clientPayMethod.receivedCoreLCRCheckbox);
expect(result).toBe('checked');
});
it('should confirm Received core VNL checkbox is unchecked', async() => {
const result = await nightmare
.checkboxState(selectors.clientPayMethod.receivedCoreVNLCheckbox);
expect(result).toBe('unchecked');
});
it('should confirm Received B2B VNL checkbox is unchecked', async() => {
const result = await nightmare
.checkboxState(selectors.clientPayMethod.receivedB2BVNLCheckbox);
expect(result).toBe('unchecked');
});
});

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare'; import createNightmare from '../../helpers/nightmare';
describe('Client risk path', () => { describe('Client balance path', () => {
const nightmare = createNightmare(); const nightmare = createNightmare();
beforeAll(() => { beforeAll(() => {
@ -19,10 +19,10 @@ describe('Client risk path', () => {
expect(result).toEqual('Data saved!'); expect(result).toEqual('Data saved!');
}); });
it('should access to the risk section to check the data shown matches the local settings', async() => { it('should access to the balance section to check the data shown matches the local settings', async() => {
let result = await nightmare let result = await nightmare
.accessToSection('client.card.risk.index') .accessToSection('client.card.balance.index')
.waitToGetProperty(`${selectors.clientRisk.companyAutocomplete} input`, 'value'); .waitToGetProperty(`${selectors.clientBalance.companyAutocomplete} input`, 'value');
expect(result).toEqual('CCs'); expect(result).toEqual('CCs');
}); });
@ -38,19 +38,19 @@ describe('Client risk path', () => {
it('should click the new payment button', async() => { it('should click the new payment button', async() => {
let url = await nightmare let url = await nightmare
.reloadSection('client.card.risk.index') .reloadSection('client.card.balance.index')
.waitToClick(selectors.clientRisk.newPaymentButton) .waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/risk') .waitForURL('/balance')
.parsedUrl(); .parsedUrl();
expect(url.hash).toContain('/risk'); expect(url.hash).toContain('/balance');
}); });
it('should create a new payment that clears the debt', async() => { it('should create a new payment that clears the debt', async() => {
let result = await nightmare let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentBankInut) .clearInput(selectors.clientBalance.newPaymentBankInut)
.write(selectors.clientRisk.newPaymentBankInut, '2') .write(selectors.clientBalance.newPaymentBankInut, '2')
.waitToClick(selectors.clientRisk.saveButton) .waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar(); .waitForLastSnackbar();
expect(result).toContain('Data saved!'); expect(result).toContain('Data saved!');
@ -58,30 +58,30 @@ describe('Client risk path', () => {
it('should check balance is now 0 and the company is now VNL becouse the user local settings were removed', async() => { it('should check balance is now 0 and the company is now VNL becouse the user local settings were removed', async() => {
let company = await nightmare let company = await nightmare
.waitToGetProperty(`${selectors.clientRisk.companyAutocomplete} input`, 'value'); .waitToGetProperty(`${selectors.clientBalance.companyAutocomplete} input`, 'value');
let firstRiskLineBalance = await nightmare let firstBalanceLine = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText'); .waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(company).toEqual('VNL'); expect(company).toEqual('VNL');
expect(firstRiskLineBalance).toContain('0.00'); expect(firstBalanceLine).toContain('0.00');
}); });
it('should now click the new payment button', async() => { it('should now click the new payment button', async() => {
let url = await nightmare let url = await nightmare
.waitToClick(selectors.clientRisk.newPaymentButton) .waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/risk') .waitForURL('/balance')
.parsedUrl(); .parsedUrl();
expect(url.hash).toContain('/risk'); expect(url.hash).toContain('/balance');
}); });
it('should create a new payment that sets the balance to positive value', async() => { it('should create a new payment that sets the balance to positive value', async() => {
let result = await nightmare let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentAmountInput) .clearInput(selectors.clientBalance.newPaymentAmountInput)
.write(selectors.clientRisk.newPaymentAmountInput, '100') .write(selectors.clientBalance.newPaymentAmountInput, '100')
.waitToClick(selectors.clientRisk.saveButton) .waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar(); .waitForLastSnackbar();
expect(result).toContain('Data saved!'); expect(result).toContain('Data saved!');
@ -89,26 +89,26 @@ describe('Client risk path', () => {
it('should check balance is now 100', async() => { it('should check balance is now 100', async() => {
let result = await nightmare let result = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText'); .waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(result).toContain('100.00'); expect(result).toContain('100.00');
}); });
it('should again click the new payment button', async() => { it('should again click the new payment button', async() => {
let url = await nightmare let url = await nightmare
.waitToClick(selectors.clientRisk.newPaymentButton) .waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/risk') .waitForURL('/balance')
.parsedUrl(); .parsedUrl();
expect(url.hash).toContain('/risk'); expect(url.hash).toContain('/balance');
}); });
it('should create a new payment that sets the balance back to the original negative value', async() => { it('should create a new payment that sets the balance back to the original negative value', async() => {
let result = await nightmare let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentAmountInput) .clearInput(selectors.clientBalance.newPaymentAmountInput)
.write(selectors.clientRisk.newPaymentAmountInput, '-150') .write(selectors.clientBalance.newPaymentAmountInput, '-150')
.wait(1999) .wait(1999)
.waitToClick(selectors.clientRisk.saveButton) .waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar(); .waitForLastSnackbar();
expect(result).toContain('Data saved!'); expect(result).toContain('Data saved!');
@ -116,7 +116,7 @@ describe('Client risk path', () => {
it('should check balance is now -50', async() => { it('should check balance is now -50', async() => {
let result = await nightmare let result = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText'); .waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(result).toContain('€50.00'); expect(result).toContain('€50.00');
}); });
@ -143,20 +143,20 @@ describe('Client risk path', () => {
expect(resultCount).toEqual(1); expect(resultCount).toEqual(1);
}); });
it(`should click on the search result to access to the client's risk`, async() => { it(`should click on the search result to access to the client's balance`, async() => {
let url = await nightmare let url = await nightmare
.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker') .waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker')
.waitToClick(selectors.clientsIndex.searchResult) .waitToClick(selectors.clientsIndex.searchResult)
.waitToClick(selectors.clientRisk.riskButton) .waitToClick(selectors.clientBalance.balanceButton)
.waitForURL('/risk') .waitForURL('/balance')
.parsedUrl(); .parsedUrl();
expect(url.hash).toContain('/risk'); expect(url.hash).toContain('/balance');
}); });
it('should not be able to click the new payment button as it isnt present', async() => { it('should not be able to click the new payment button as it isnt present', async() => {
let result = await nightmare let result = await nightmare
.exists(selectors.clientRisk.newPaymentButton); .exists(selectors.clientBalance.newPaymentButton);
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });

View File

@ -84,7 +84,7 @@ describe('Item summary path', () => {
const result = await nightmare const result = await nightmare
.clearInput('vn-item-index vn-searchbar input') .clearInput('vn-item-index vn-searchbar input')
.waitToClick(selectors.itemsIndex.searchButton) .waitToClick(selectors.itemsIndex.searchButton)
.write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat first 15cm') .write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat fist 15cm')
.waitToClick(selectors.itemsIndex.searchButton) .waitToClick(selectors.itemsIndex.searchButton)
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1) .waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
.countElement(selectors.itemsIndex.searchResult); .countElement(selectors.itemsIndex.searchResult);
@ -94,7 +94,7 @@ describe('Item summary path', () => {
it(`should now click on the search result summary button to open the item summary popup`, async() => { it(`should now click on the search result summary button to open the item summary popup`, async() => {
const isVisibleBefore = await nightmare const isVisibleBefore = await nightmare
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Melee weapon combat first 15cm') .waitForTextInElement(selectors.itemsIndex.searchResult, 'Melee weapon combat fist 15cm')
.isVisible(selectors.itemSummary.basicData); .isVisible(selectors.itemSummary.basicData);
const isVisibleAfter = await nightmare const isVisibleAfter = await nightmare
@ -108,10 +108,10 @@ describe('Item summary path', () => {
it(`should now check the item summary preview shows fields from basic data`, async() => { it(`should now check the item summary preview shows fields from basic data`, async() => {
const result = await nightmare const result = await nightmare
.waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat first 15cm') .waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat fist 15cm')
.waitToGetProperty(selectors.itemSummary.basicData, 'innerText'); .waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
expect(result).toContain('Melee weapon combat first 15cm'); expect(result).toContain('Melee weapon combat fist 15cm');
}); });
it(`should now check the item summary preview shows fields from tags`, async() => { it(`should now check the item summary preview shows fields from tags`, async() => {
@ -172,10 +172,10 @@ describe('Item summary path', () => {
it(`should check the item summary shows fields from basic data section`, async() => { it(`should check the item summary shows fields from basic data section`, async() => {
const result = await nightmare const result = await nightmare
.waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat first 15cm') .waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat fist 15cm')
.waitToGetProperty(selectors.itemSummary.basicData, 'innerText'); .waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
expect(result).toContain('Melee weapon combat first 15cm'); expect(result).toContain('Melee weapon combat fist 15cm');
}); });
it(`should check the item summary shows fields from tags section`, async() => { it(`should check the item summary shows fields from tags section`, async() => {

View File

@ -7,8 +7,8 @@ describe('Item Edit basic data path', () => {
beforeAll(() => { beforeAll(() => {
nightmare nightmare
.loginAndModule('buyer', 'item') .loginAndModule('buyer', 'item')
.accessToSearchResult('Melee weapon combat first 15cm') .accessToSearchResult('Melee weapon combat fist 15cm')
.accessToSection('item.card.data'); .accessToSection('item.card.basicData');
}); });
it(`should check the descritor edit button is visible for buyer`, async() => { it(`should check the descritor edit button is visible for buyer`, async() => {
@ -39,7 +39,7 @@ describe('Item Edit basic data path', () => {
it(`should confirm the item name was edited`, async() => { it(`should confirm the item name was edited`, async() => {
const result = await nightmare const result = await nightmare
.reloadSection('item.card.data') .reloadSection('item.card.basicData')
.waitToGetProperty(selectors.itemBasicData.nameInput, 'value'); .waitToGetProperty(selectors.itemBasicData.nameInput, 'value');
expect(result).toEqual('Rose of Purity'); expect(result).toEqual('Rose of Purity');

View File

@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare'; import createNightmare from '../../helpers/nightmare';
// #1186 repearar e2e ticket.sale, item.regularize. describe('Item regularize path', () => {
xdescribe('Item regularize path', () => {
const nightmare = createNightmare(); const nightmare = createNightmare();
beforeAll(() => { beforeAll(() => {
nightmare nightmare
@ -177,7 +176,7 @@ xdescribe('Item regularize path', () => {
it('should search for the ticket with id 23 once again', async() => { it('should search for the ticket with id 23 once again', async() => {
const result = await nightmare const result = await nightmare
.write(selectors.ticketsIndex.searchTicketInput, 'id:24') .write(selectors.ticketsIndex.searchTicketInput, 23)
.waitToClick(selectors.ticketsIndex.searchButton) .waitToClick(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1) .waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countElement(selectors.ticketsIndex.searchResult); .countElement(selectors.ticketsIndex.searchResult);
@ -187,7 +186,7 @@ xdescribe('Item regularize path', () => {
it(`should now click on the search result to access to the ticket summary`, async() => { it(`should now click on the search result to access to the ticket summary`, async() => {
const url = await nightmare const url = await nightmare
.waitForTextInElement(selectors.ticketsIndex.searchResult, '24') .waitForTextInElement(selectors.ticketsIndex.searchResult, '23')
.waitToClick(selectors.ticketsIndex.searchResult) .waitToClick(selectors.ticketsIndex.searchResult)
.waitForURL('/summary') .waitForURL('/summary')
.parsedUrl(); .parsedUrl();

View File

@ -7,7 +7,7 @@ describe('Item descriptor path', () => {
nightmare nightmare
.loginAndModule('buyer', 'item') .loginAndModule('buyer', 'item')
.accessToSearchResult(1) .accessToSearchResult(1)
.accessToSection('item.card.data'); .accessToSection('item.card.basicData');
}); });
it('should check the descriptor inactive icon is dark as the item is active', async() => { it('should check the descriptor inactive icon is dark as the item is active', async() => {
@ -30,7 +30,7 @@ describe('Item descriptor path', () => {
it('should reload the section and check the inactive icon is bright', async() => { it('should reload the section and check the inactive icon is bright', async() => {
let brightIcon = await nightmare let brightIcon = await nightmare
.reloadSection('item.card.data') .reloadSection('item.card.basicData')
.waitForClassPresent(selectors.itemDescriptor.inactiveIcon, 'bright') .waitForClassPresent(selectors.itemDescriptor.inactiveIcon, 'bright')
.isVisible(selectors.itemDescriptor.inactiveIcon); .isVisible(selectors.itemDescriptor.inactiveIcon);

View File

@ -8,7 +8,7 @@ describe('Ticket Create packages path', () => {
return nightmare return nightmare
.loginAndModule('employee', 'ticket') .loginAndModule('employee', 'ticket')
.accessToSearchResult('id:1') .accessToSearchResult('id:1')
.accessToSection('ticket.card.package.index'); .accessToSection('ticket.card.package');
}); });
it(`should attempt create a new package but receive an error if package is blank`, async() => { it(`should attempt create a new package but receive an error if package is blank`, async() => {
@ -53,7 +53,7 @@ describe('Ticket Create packages path', () => {
it(`should confirm the first select is the expected one`, async() => { it(`should confirm the first select is the expected one`, async() => {
const result = await nightmare const result = await nightmare
.reloadSection('ticket.card.package.index') .reloadSection('ticket.card.package')
.waitForTextInInput(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'Container medical box 1m') .waitForTextInInput(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'Container medical box 1m')
.waitToGetProperty(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'value'); .waitToGetProperty(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'value');

View File

@ -88,7 +88,7 @@ describe('Ticket Create new tracking state path', () => {
it(`should create a new state with all it's data`, async() => { it(`should create a new state with all it's data`, async() => {
let result = await nightmare let result = await nightmare
.autocompleteSearch(selectors.createStateView.workerAutocomplete, 'accessory') .autocompleteSearch(selectors.createStateView.workerAutocomplete, 'replenisher')
.waitToClick(selectors.createStateView.saveStateButton) .waitToClick(selectors.createStateView.saveStateButton)
.waitForLastSnackbar(); .waitForLastSnackbar();

View File

@ -8,7 +8,7 @@ describe('Ticket Edit basic data path', () => {
return nightmare return nightmare
.loginAndModule('employee', 'ticket') .loginAndModule('employee', 'ticket')
.accessToSearchResult('id:11') .accessToSearchResult('id:11')
.accessToSection('ticket.card.data.stepOne'); .accessToSection('ticket.card.basicData.stepOne');
}); });
it(`should edit the client and address of the ticket then click next`, async() => { it(`should edit the client and address of the ticket then click next`, async() => {

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare'; import createNightmare from '../../helpers/nightmare';
// #1186 repearar e2e ticket.sale, item.regularize. // #1387 e2e Ticket Edit sale path
xdescribe('Ticket Edit sale path', () => { xdescribe('Ticket Edit sale path', () => {
const nightmare = createNightmare(); const nightmare = createNightmare();

View File

@ -148,4 +148,43 @@ describe('Ticket descriptor path', () => {
expect(exists).toBeFalsy(); expect(exists).toBeFalsy();
}); });
}); });
describe('Make invoice', () => {
it('should login as adminBoss role then search for a ticket', async() => {
const invoiceableTicketId = 11;
const url = await nightmare
.loginAndModule('adminBoss', 'ticket')
.accessToSearchResult(invoiceableTicketId)
.waitForURL('/summary')
.parsedUrl();
expect(url.hash).toContain(`ticket/${invoiceableTicketId}/summary`);
});
it(`should make sure the ticket doesn't have an invoiceOutFk yet`, async() => {
const result = await nightmare
.waitToGetProperty(selectors.ticketSummary.invoiceOutRef, 'innerText');
expect(result).toEqual('-');
});
it('should invoice the ticket using the descriptor more menu', async() => {
const result = await nightmare
.waitToClick(selectors.ticketDescriptor.moreMenu)
.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice)
.waitToClick(selectors.ticketDescriptor.acceptInvoiceOutButton)
.waitForLastSnackbar();
expect(result).toEqual('Ticket invoiced');
});
it(`should make sure the ticket summary have an invoiceOutFk`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.ticketSummary.invoiceOutRef, 'T4444445')
.waitToGetProperty(selectors.ticketSummary.invoiceOutRef, 'innerText');
expect(result).toEqual('T4444445');
});
});
}); });

View File

@ -7,7 +7,7 @@ describe('Ticket services path', () => {
beforeAll(() => { beforeAll(() => {
nightmare nightmare
.loginAndModule('employee', 'ticket') .loginAndModule('employee', 'ticket')
.accessToSearchResult('1') .accessToSearchResult(8)
.accessToSection('ticket.card.service'); .accessToSection('ticket.card.service');
}); });

View File

@ -3,7 +3,7 @@ import Component from '../../lib/component';
import './style.scss'; import './style.scss';
/** /**
* Calendar. * Flat calendar.
* *
*/ */
export default class Calendar extends Component { export default class Calendar extends Component {

View File

@ -1,5 +1,4 @@
// #937 front test datePicker awaiting loopback connector refactor to store dates correctly. describe('Component vnDatePicker', () => {
xdescribe('Component vnDatePicker', () => {
let controller; let controller;
let $attrs; let $attrs;
let $element; let $element;

View File

@ -277,6 +277,7 @@ export default class DropDown extends Component {
if (data) { if (data) {
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
let option = data[i]; let option = data[i];
option.orgShowField = option[this.showField];
if (this.translateFields) { if (this.translateFields) {
option = Object.assign({}, option); option = Object.assign({}, option);
@ -285,6 +286,8 @@ export default class DropDown extends Component {
} }
let li = this.document.createElement('li'); let li = this.document.createElement('li');
li.setAttribute('name', option.orgShowField);
fragment.appendChild(li); fragment.appendChild(li);
if (this.multiple) { if (this.multiple) {

View File

@ -1 +1,5 @@
<vn-check field="$ctrl.checkAll"></vn-check> <vn-check field="$ctrl.checked"
intermediate="$ctrl.isIntermediate"
vn-tooltip="Check all"
tooltip-position="up">
</vn-check>

View File

@ -10,23 +10,104 @@ import Input from '../../lib/input';
export default class MultiCheck extends Input { export default class MultiCheck extends Input {
constructor($element, $scope) { constructor($element, $scope) {
super($element, $scope); super($element, $scope);
this._checkAll = false; this._checked = false;
this.checkField = 'checked'; this.checkField = 'checked';
this.isIntermediate = false;
} }
get checkAll() { /**
return this._checkAll; * Gets array model instance
*
* @return {ArrayModel} - Array model instance
*/
get model() {
return this._model;
} }
set checkAll(value) { /**
this._checkAll = value; * Sets the array model instance
this.switchChecks(); * Changes intermediate property for
* the check component
*
* @param {ArrayModel} value - Array model instance
*/
set model(value) {
this._model = value;
if (value) {
value.on('rowChange', () => {
this.isIntermediate = !this.areAllUnchecked() && !this.areAllChecked();
if (this.areAllChecked())
this._checked = true;
else if (!this.areAllChecked())
this._checked = false;
});
value.on('dataChange', () => {
if (this.checked) this.toggle();
});
}
} }
switchChecks() { /**
if (!this.data) return; * Gets current check state
this.data.forEach(el => { */
el[this.checkField] = this._checkAll; get checked() {
return this._checked;
}
/**
* Sets current check state
*
* @param {Boolean} value - Checkbox state [undefined, true, false]
*/
set checked(value) {
this._checked = value;
this.checkAll = value;
this.toggle();
}
/**
* Returns a bolean result for
* checked instances
*
* @return {Boolean} - True if all instances are checked
*/
areAllChecked() {
if (!this.model || !this.model.data) return;
const data = this.model.data;
return data.every(item => {
return item[this.checkField] === true;
});
}
/**
* Returns a bolean result for
* checked instances
*
* @return {Boolean} - False if all instances are checked
*/
areAllUnchecked() {
if (!this.model || !this.model.data) return;
const data = this.model.data;
return data.every(item => {
return item[this.checkField] === false;
});
}
/**
* Toggles checked property on
* all instances
*/
toggle() {
const data = this.model.data;
if (!data) return;
data.forEach(el => {
el[this.checkField] = this.checkAll;
}); });
} }
} }
@ -35,7 +116,7 @@ ngModule.component('vnMultiCheck', {
template: require('./multi-check.html'), template: require('./multi-check.html'),
controller: MultiCheck, controller: MultiCheck,
bindings: { bindings: {
data: '=', model: '<',
checkField: '<?', checkField: '<?',
checkAll: '=?', checkAll: '=?',
disabled: '<?' disabled: '<?'

View File

@ -1,4 +1,5 @@
import './multi-check.js'; import './multi-check.js';
import crudModel from 'core/mocks/crud-model';
describe('Component vnMultiCheck', () => { describe('Component vnMultiCheck', () => {
let controller; let controller;
@ -9,33 +10,94 @@ describe('Component vnMultiCheck', () => {
beforeEach(angular.mock.inject($componentController => { beforeEach(angular.mock.inject($componentController => {
$element = angular.element(`<div class="shown"></div>`); $element = angular.element(`<div class="shown"></div>`);
controller = $componentController('vnMultiCheck', {$element: $element}); controller = $componentController('vnMultiCheck', {$element: $element});
controller._model = crudModel;
controller._model.data = [
{id: 1, name: 'My item 1'},
{id: 2, name: 'My item 2'},
{id: 3, name: 'My item 3'}
];
})); }));
describe('checkAll() setter', () => { describe('checked() setter', () => {
it(`should set controller _checkAll property with the argument received then call switchChecks()`, () => { it(`should set controller _checked property with the argument received then call toggle()`, () => {
let argument = 'I am the model'; spyOn(controller, 'toggle');
spyOn(controller, 'switchChecks'); controller.checked = crudModel;
controller.checkAll = argument;
expect(controller._checkAll).toEqual(argument); expect(controller._checked).toEqual(crudModel);
expect(controller.switchChecks).toHaveBeenCalledWith(); expect(controller.toggle).toHaveBeenCalledWith();
}); });
}); });
describe('switchChecks()', () => { describe('toggle()', () => {
it(`should set checked property inside each existing element`, () => { it(`should set checked property inside each existing element`, () => {
controller.data = [ const data = controller.model.data;
{name: 'name'},
{name: null}
];
expect(controller.data[0].checked).not.toBeDefined(); expect(data[0].checked).not.toBeDefined();
expect(controller.data[1].checked).not.toBeDefined(); expect(data[1].checked).not.toBeDefined();
controller._checkAll = 1; expect(data[2].checked).not.toBeDefined();
controller.switchChecks();
expect(controller.data[0].checked).toBeTruthy(); controller._checked = true;
expect(controller.data[1].checked).toBeTruthy(); controller.checkAll = true;
controller.toggle();
expect(data[0].checked).toBeTruthy();
expect(data[1].checked).toBeTruthy();
expect(data[2].checked).toBeTruthy();
});
it(`should unset checked property inside each existing element`, () => {
const data = controller.model.data;
data[0].checked = true;
data[1].checked = true;
data[2].checked = true;
controller._checked = false;
controller.checkAll = false;
controller.toggle();
expect(data[0].checked).toBeFalsy();
expect(data[1].checked).toBeFalsy();
expect(data[2].checked).toBeFalsy();
});
});
describe('areAllChecked()', () => {
it(`should set return true if all elements are checked`, () => {
const data = controller.model.data;
data[0].checked = true;
data[1].checked = true;
data[2].checked = true;
expect(controller.areAllChecked()).toBeTruthy();
});
it(`should set return false if not all elements are checked`, () => {
const data = controller.model.data;
data[0].checked = true;
data[1].checked = false;
data[2].checked = true;
expect(controller.areAllChecked()).toBeFalsy();
});
});
describe('areAllUnchecked()', () => {
it(`should set return true if all elements are unchecked`, () => {
const data = controller.model.data;
data[0].checked = false;
data[1].checked = false;
data[2].checked = false;
expect(controller.areAllUnchecked()).toBeTruthy();
});
it(`should set return false if not all elements are unchecked`, () => {
const data = controller.model.data;
data[0].checked = false;
data[1].checked = true;
data[2].checked = false;
expect(controller.areAllUnchecked()).toBeFalsy();
}); });
}); });
}); });

View File

@ -5,14 +5,13 @@
'collapsed': !item.active, 'collapsed': !item.active,
'included': item.selected == 1, 'included': item.selected == 1,
'excluded': item.selected == 0 'excluded': item.selected == 0
}"> }" vn-draggable="{{::$ctrl.draggable}}" vn-droppable="{{::$ctrl.droppable}}" on-drop="$ctrl.onDrop(item, dragged, dropped)">
<vn-horizontal> <vn-horizontal>
<vn-auto class="actions"> <vn-auto class="actions">
<vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}" <vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}"
ng-click="$ctrl.toggle(item, $event)"> ng-click="$ctrl.toggle(item, $event)">
</vn-icon> </vn-icon>
</vn-auto> </vn-auto>
<vn-one class="description">
<vn-check vn-auto vn-acl="{{$ctrl.aclRole}}" <vn-check vn-auto vn-acl="{{$ctrl.aclRole}}"
ng-if="$ctrl.selectable" ng-if="$ctrl.selectable"
field="item.selected" field="item.selected"
@ -20,12 +19,11 @@
on-change="$ctrl.select(item, value)" on-change="$ctrl.select(item, value)"
triple-state="true"> triple-state="true">
</vn-check> </vn-check>
{{::item.name}} <vn-one class="description">{{::item.name}}</vn-one>
</vn-one>
<vn-auto> <vn-auto>
<vn-icon-button icon="{{icon.icon}}" <vn-icon-button icon="{{icon.icon}}"
ng-repeat="icon in $ctrl.icons" ng-repeat="icon in $ctrl.icons"
ng-click="$ctrl.onClick(icon, item, $ctrl.parent, $parent.$index)" ng-click="$ctrl.onIconClick(icon, item, $ctrl.parent, $parent.$index)"
vn-acl="{{$ctrl.aclRole}}" vn-acl-action="remove"> vn-acl="{{$ctrl.aclRole}}" vn-acl-action="remove">
</vn-icon-button> </vn-icon-button>
</vn-auto> </vn-auto>
@ -34,7 +32,10 @@
selectable="$ctrl.selectable" selectable="$ctrl.selectable"
disabled="$ctrl.disabled" disabled="$ctrl.disabled"
editable="$ctrl.editable" editable="$ctrl.editable"
icons="$ctrl.icons" draggable="::$ctrl.draggable"
droppable="::$ctrl.droppable"
icons="::$ctrl.icons"
parent-scope="::$ctrl.parentScope"
acl-role="$ctrl.aclRole"> acl-role="$ctrl.aclRole">
</vn-treeview-child> </vn-treeview-child>
</li> </li>

View File

@ -15,9 +15,8 @@ class Controller extends Component {
this.treeview.onSelection(item, value); this.treeview.onSelection(item, value);
} }
onClick(icon, item, parent, index) { onIconClick(icon, item, parent, index) {
let parentScope = this.$scope.$parent.$parent; let parentController = this.parentScope.$ctrl;
let parentController = parentScope.$ctrl;
icon.callback.call(parentController, item, parent, index); icon.callback.call(parentController, item, parent, index);
} }
@ -25,6 +24,10 @@ class Controller extends Component {
this.treeview.onCreate(parent); this.treeview.onCreate(parent);
} }
onDrop(item, dragged, dropped) {
this.treeview.onDrop(item, dragged, dropped);
}
get isInsertable() { get isInsertable() {
return Array.isArray(this.parent) || this.parent.childs; return Array.isArray(this.parent) || this.parent.childs;
} }
@ -40,7 +43,10 @@ ngModule.component('vnTreeviewChild', {
disabled: '<?', disabled: '<?',
selectable: '<?', selectable: '<?',
editable: '<?', editable: '<?',
draggable: '<?',
droppable: '<?',
aclRole: '<?', aclRole: '<?',
parentScope: '<'
}, },
require: { require: {
treeview: '^vnTreeview' treeview: '^vnTreeview'

View File

@ -1,9 +1,12 @@
<vn-treeview-child <vn-treeview-child acl-role="$ctrl.aclRole"
items="$ctrl.data" items="$ctrl.data"
parent="$ctrl.data" parent="$ctrl.data"
selectable="$ctrl.selectable" selectable="$ctrl.selectable"
editable="$ctrl.editable" editable="$ctrl.editable"
disabled="$ctrl.disabled" disabled="$ctrl.disabled"
icons="$ctrl.icons" icons="$ctrl.icons"
acl-role="$ctrl.aclRole"> parent-scope="$ctrl.$scope.$parent"
draggable="$ctrl.draggable"
droppable="$ctrl.droppable"
vn-droppable="{{$ctrl.droppable}}">
</vn-treeview-child> </vn-treeview-child>

View File

@ -3,13 +3,14 @@ import Component from '../../lib/component';
import './style.scss'; import './style.scss';
/** /**
* A simple tooltip. * Treeview
* *
* @property {String} position The relative position to the parent * @property {String} position The relative position to the parent
*/ */
export default class Treeview extends Component { export default class Treeview extends Component {
constructor($element, $scope) { constructor($element, $scope) {
super($element, $scope); super($element, $scope);
this.$scope = $scope;
this.data = []; this.data = [];
} }
@ -69,6 +70,10 @@ export default class Treeview extends Component {
item.active = !item.active; item.active = !item.active;
} }
onDrop(item, dragged, dropped) {
this.emit('drop', {item, dragged, dropped});
}
} }
Treeview.$inject = ['$element', '$scope']; Treeview.$inject = ['$element', '$scope'];
@ -82,6 +87,8 @@ ngModule.component('vnTreeview', {
disabled: '<?', disabled: '<?',
selectable: '<?', selectable: '<?',
editable: '<?', editable: '<?',
draggable: '<?',
droppable: '<?',
aclRole: '@?' aclRole: '@?'
} }
}); });

View File

@ -1,6 +1,10 @@
@import "variables"; @import "variables";
vn-treeview { vn-treeview {
vn-treeview-child {
display: block
}
ul { ul {
line-height: 24px; line-height: 24px;
padding: 0; padding: 0;
@ -9,17 +13,21 @@ vn-treeview {
li { li {
list-style: none; list-style: none;
cursor: pointer;
.actions { .actions {
min-width: 24px; min-width: 24px;
} }
.description { .description {
pointer-events: none;
padding-left: 5px padding-left: 5px
} }
} }
li vn-icon {
cursor: pointer;
}
li ul { li ul {
padding-left: 1.8em; padding-left: 1.8em;
} }
@ -46,19 +54,22 @@ vn-treeview {
& > vn-horizontal > .description { & > vn-horizontal > .description {
color: $color-notice; color: $color-notice;
font-weight: bold; font-weight: bold;
}
& > vn-check .md-icon { & > vn-horizontal > vn-check .md-icon {
background-color: $color-notice background-color: $color-notice
} }
} }
}
li.excluded { li.excluded {
& > vn-horizontal > .description { & > vn-horizontal > .description {
color: $color-alert; color: $color-alert;
font-weight: bold; font-weight: bold;
}
& > vn-horizontal > vn-check .md-icon {
background-color: $color-alert;
border-color: transparent
} }
} }
} }

View File

@ -0,0 +1,43 @@
import ngModule from '../module';
/**
* Enables a draggable element and his drag events
*
* @return {Object} The directive
*/
export function directive() {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
const element = $element[0];
const isDraggable = $attrs.vnDraggable === 'true';
if (!isDraggable) return;
// Set draggable style properties
element.style.cursor = 'move';
// Enable as draggable element
element.setAttribute('draggable', true);
/**
* Fires when a drag event starts
*/
element.addEventListener('dragstart', event => {
element.style.opacity = 0.5;
event.stopPropagation();
});
/**
* Fires when a drag event ends
*/
element.addEventListener('dragend', event => {
element.style.opacity = 1;
event.stopPropagation();
});
}
};
}
ngModule.directive('vnDraggable', directive);

View File

@ -0,0 +1,93 @@
import ngModule from '../module';
export function directive($parse) {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
const element = $element[0];
const onDropEvent = $parse($attrs.onDrop);
const isDroppable = $attrs.vnDroppable === 'true';
if (!isDroppable) return;
/**
* Captures current dragging element
*/
element.addEventListener('dragstart', () => {
this.dragged = element;
});
/**
* Enter droppable area event
*/
element.addEventListener('dragenter', event => {
element.style.backgroundColor = 'yellow';
event.stopImmediatePropagation();
event.preventDefault();
}, false);
/**
* Exit droppable area event
*/
element.addEventListener('dragleave', event => {
element.style.backgroundColor = 'transparent';
event.stopImmediatePropagation();
event.preventDefault();
});
/**
* Prevent dragover for allowing
* dispatch drop event
*/
element.addEventListener('dragover', event => {
event.stopPropagation();
event.preventDefault();
});
/**
* Fires when a drop events
*/
element.addEventListener('drop', event => {
const draggedParent = this.dragged.parentNode;
const targetChild = element.querySelector('ul');
element.style.transition = 'background 2s';
element.style.backgroundColor = 'transparent';
if (this.dragged === element)
return event.preventDefault();
if (targetChild) {
const targetNodes = targetChild.querySelectorAll('li');
const before = targetNodes[targetNodes.length - 1];
targetChild.insertBefore(this.dragged, before);
} else
draggedParent.removeChild(this.dragged);
onDropEvent($scope, {
dragged: {
element: this.dragged,
scope: angular.element(this.dragged).scope()
},
dropped: {
element: element,
scope: angular.element(element).scope()
}
});
event.stopImmediatePropagation();
});
}
};
}
directive.$inject = ['$parse'];
ngModule.directive('vnDroppable', directive);

View File

@ -11,3 +11,5 @@ import './bind';
import './repeat-last'; import './repeat-last';
import './title'; import './title';
import './uvc'; import './uvc';
import './draggable';
import './droppable';

View File

@ -42,3 +42,4 @@ Loading: Cargando
Fields to show: Campos a mostrar Fields to show: Campos a mostrar
Create new one: Crear nuevo Create new one: Crear nuevo
Toggle: Desplegar/Plegar Toggle: Desplegar/Plegar
Check all: Seleccionar todo

View File

@ -24,7 +24,14 @@ module.exports = {
} }
}; };
}, },
on: () => {
return {
then: callback => {
callback({data: {id: 1234}});
}
};
},
refresh: () => {}, refresh: () => {},
addFilter: () => {}, addFilter: () => {},
applyFilter: () => {} applyFilter: () => {},
}; };

View File

@ -24,5 +24,5 @@ window.ngModule = function(moduleName) {
let testsContext = require.context('./', true, /\.spec\.js$/); let testsContext = require.context('./', true, /\.spec\.js$/);
testsContext.keys().forEach(testsContext); testsContext.keys().forEach(testsContext);
let modulesContext = require.context('../modules/', true, /^\.\/[a-z0-9]+\/front\/.+\.spec\.js$/); let modulesContext = require.context('../modules/', true, /^\.\/[a-zA-Z0-9-]+\/front\/.+\.spec\.js$/);
modulesContext.keys().forEach(modulesContext); modulesContext.keys().forEach(modulesContext);

View File

@ -41,5 +41,7 @@
"City cannot be empty": "City cannot be empty", "City cannot be empty": "City cannot be empty",
"EXTENSION_INVALID_FORMAT": "Invalid extension", "EXTENSION_INVALID_FORMAT": "Invalid extension",
"The secret can't be blank": "The secret can't be blank", "The secret can't be blank": "The secret can't be blank",
"Invalid TIN": "Invalid Tax number" "Invalid TIN": "Invalid Tax number",
"This ticket can't be invoiced": "This ticket can't be invoiced",
"The value should be a number": "The value should be a number"
} }

View File

@ -79,6 +79,7 @@
"We weren't able to send this SMS": "No hemos podido enviar el SMS", "We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado", "This client can't be invoiced": "Este cliente no puede ser facturado",
"This ticket can't be invoiced": "Este ticket no puede ser facturado", "This ticket can't be invoiced": "Este ticket no puede ser facturado",
"That item is not available on that day": "That item is not available on that day", "That item is not available on that day": "El item no esta disponible para esa fecha",
"That item doesn't exists": "That item doesn't exists" "That item doesn't exists": "That item doesn't exists",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado"
} }

View File

@ -1,7 +1,6 @@
import './index.js'; import './index.js';
xdescribe('Agency', () => { describe('Agency Component vnZoneCard', () => {
describe('Component vnZoneCard', () => {
let $scope; let $scope;
let controller; let controller;
let $httpBackend; let $httpBackend;
@ -33,5 +32,4 @@ xdescribe('Agency', () => {
}); });
}); });
}); });
});

View File

@ -1,8 +1,7 @@
import './index'; import './index';
import watcher from 'core/mocks/watcher'; import watcher from 'core/mocks/watcher';
xdescribe('Agency', () => { describe('Agency Component vnZoneCreate', () => {
describe('Component vnZoneCreate', () => {
let $scope; let $scope;
let $state; let $state;
let controller; let controller;
@ -33,8 +32,8 @@ xdescribe('Agency', () => {
controller.onSubmit(); controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('zone.card.basicData', {id: 1234}); expect(controller.$state.go).toHaveBeenCalledWith('zone.card.location', Object({id: 1234}));
});
}); });
}); });
}); });

View File

@ -1,7 +1,6 @@
import './index.js'; import './index.js';
xdescribe('Agency', () => { describe('Agency Component vnZoneIndex', () => {
describe('Component vnZoneIndex', () => {
let $componentController; let $componentController;
let controller; let controller;
@ -29,7 +28,7 @@ xdescribe('Agency', () => {
expect(result).toEqual({warehouseFk: 'Silla'}); expect(result).toEqual({warehouseFk: 'Silla'});
}); });
it('should return a formated object with the warehouseFk in case of warehouseFk', () => { it('should return a formated object with the warehouseFk in case of agencyModeFk', () => {
let param = 'agencyModeFk'; let param = 'agencyModeFk';
let value = 'My Delivery'; let value = 'My Delivery';
let result = controller.exprBuilder(param, value); let result = controller.exprBuilder(param, value);
@ -38,4 +37,3 @@ xdescribe('Agency', () => {
}); });
}); });
}); });
});

View File

@ -12,8 +12,9 @@
on-search="$ctrl.onSearch()" on-search="$ctrl.onSearch()"
vn-focus> vn-focus>
</vn-searchbar> </vn-searchbar>
<vn-treeview vn-id="treeview" model="model" selectable="true" acl-role="deliveryBoss" <vn-treeview vn-id="treeview" model="model" acl-role="deliveryBoss"
on-selection="$ctrl.onSelection(item, value)"> on-selection="$ctrl.onSelection(item, value)"
selectable="true">
</vn-treeview> </vn-treeview>
</vn-card> </vn-card>
<vn-side-menu side="right"> <vn-side-menu side="right">

View File

@ -99,7 +99,7 @@ module.exports = Self => {
let stmt; let stmt;
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`SELECT cl.id, c.name, u.nickName, cs.description, cl.created `SELECT cl.id, c.name, cl.clientFk, cl.workerFk, u.nickName, cs.description, cl.created
FROM claim cl FROM claim cl
LEFT JOIN client c ON c.id = cl.clientFk LEFT JOIN client c ON c.id = cl.clientFk
LEFT JOIN worker w ON w.id = cl.workerFk LEFT JOIN worker w ON w.id = cl.workerFk

View File

@ -161,11 +161,13 @@ describe('claim', () => {
describe('onUpdateGreugeResponse()', () => { describe('onUpdateGreugeResponse()', () => {
it('should do nothing', () => { it('should do nothing', () => {
spyOn(controller.$http, 'post');
spyOn(controller.card, 'reload'); spyOn(controller.card, 'reload');
spyOn(controller.vnApp, 'showSuccess'); spyOn(controller.vnApp, 'showSuccess');
controller.onUpdateGreugeResponse('CANCEL'); controller.onUpdateGreugeResponse('CANCEL');
expect(controller.$http.post).not.toHaveBeenCalledWith();
expect(controller.card.reload).not.toHaveBeenCalledWith(); expect(controller.card.reload).not.toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).not.toHaveBeenCalledWith('Greuge inserted!'); expect(controller.vnApp.showSuccess).not.toHaveBeenCalledWith('Greuge inserted!');
}); });

View File

@ -26,7 +26,7 @@
value="{{$ctrl.claim.claimState.description}}"> value="{{$ctrl.claim.claimState.description}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Created" <vn-label-value label="Created"
value="{{$ctrl.claim.created | dateTime: 'dd/MM/yyyy'}}"> value="{{$ctrl.claim.created | dateTime: 'dd/MM/yyyy HH:mm'}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Salesperson" <vn-label-value label="Salesperson"
value="{{$ctrl.claim.client.salesPerson.user.nickname}}"> value="{{$ctrl.claim.client.salesPerson.user.nickname}}">

View File

@ -35,7 +35,7 @@
ui-sref="claim.card.summary({id: claim.id})"> ui-sref="claim.card.summary({id: claim.id})">
<vn-td number>{{::claim.id}}</vn-td> <vn-td number>{{::claim.id}}</vn-td>
<vn-td expand> <vn-td expand>
<span class="link" ng-click="$ctrl.showClientDescriptor($event, claim.client.id)"> <span class="link" ng-click="$ctrl.showClientDescriptor($event, claim.clientFk)">
{{::claim.name}} {{::claim.name}}
</span> </span>
</vn-td> </vn-td>
@ -43,7 +43,7 @@
<vn-td expand> <vn-td expand>
<span <span
class="link" class="link"
ng-click="$ctrl.showWorkerDescriptor($event, claim.worker.user.id)"> ng-click="$ctrl.showWorkerDescriptor($event, claim.workerFk)">
{{::claim.nickName}} {{::claim.nickName}}
</span> </span>
</vn-td> </vn-td>
@ -68,7 +68,7 @@
<vn-client-descriptor-popover vn-id="clientDescriptor"></vn-client-descriptor-popover> <vn-client-descriptor-popover vn-id="clientDescriptor"></vn-client-descriptor-popover>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor" vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker"> worker-fk="$ctrl.selectedWorker">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-dialog class="dialog-summary" <vn-dialog class="dialog-summary"
vn-id="dialog-summary-claim"> vn-id="dialog-summary-claim">

View File

@ -25,10 +25,10 @@ export default class Controller {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
} }
showWorkerDescriptor(event, userId) { showWorkerDescriptor(event, workerFk) {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
this.selectedWorker = userId; this.selectedWorker = workerFk;
this.$.workerDescriptor.parent = event.target; this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show(); this.$.workerDescriptor.show();
} }

View File

@ -93,7 +93,7 @@
<vn-td expand> <vn-td expand>
<span <span
class="link" class="link"
ng-click="$ctrl.showWorkerDescriptor($event, development.worker.user.id)"> ng-click="$ctrl.showWorkerDescriptor($event, development.workerFk)">
{{::development.worker.user.nickname}} {{::development.worker.user.nickname}}
</span> </span>
</vn-td> </vn-td>
@ -156,7 +156,7 @@
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor" vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker"> worker-fk="$ctrl.selectedWorker">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-ticket-descriptor-popover <vn-ticket-descriptor-popover
vn-id="ticketDescriptor"> vn-id="ticketDescriptor">

View File

@ -32,8 +32,8 @@ class Controller {
this.$.itemDescriptor.show(); this.$.itemDescriptor.show();
} }
showWorkerDescriptor(event, userId) { showWorkerDescriptor(event, workerFk) {
this.selectedWorker = userId; this.selectedWorker = workerFk;
this.$.workerDescriptor.parent = event.target; this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show(); this.$.workerDescriptor.show();
} }

View File

@ -16,14 +16,14 @@ module.exports = function(Self) {
Self.listWorkers = function() { Self.listWorkers = function() {
let query = let query =
`SELECT w.id, `SELECT w.id,
CONCAT(w.firstName, IFNULL(CONCAT(" ", w.name), "")) \`name\` CONCAT(w.firstName, IFNULL(CONCAT(" ", w.lastName), "")) \`name\`
FROM worker w FROM worker w
JOIN account.user u ON w.userFk = u.id JOIN account.user u ON w.userFk = u.id
JOIN account.roleRole rr ON rr.role = u.role JOIN account.roleRole rr ON rr.role = u.role
JOIN account.role r ON r.id = rr.inheritsFrom JOIN account.role r ON r.id = rr.inheritsFrom
WHERE u.active WHERE u.active
AND r.\`name\` = 'employee' AND r.\`name\` = 'employee'
ORDER BY w.name ASC`; ORDER BY w.lastName ASC`;
return Self.rawSql(query); return Self.rawSql(query);
}; };

View File

@ -7,6 +7,6 @@ describe('Client get', () => {
expect(result.id).toEqual(101); expect(result.id).toEqual(101);
expect(result.name).toEqual('Bruce Wayne'); expect(result.name).toEqual('Bruce Wayne');
expect(result.debt).toEqual(329.13); expect(result.debt).toEqual(-14.78);
}); });
}); });

View File

@ -4,7 +4,7 @@ describe('client getDebt()', () => {
it('should return the client debt', async() => { it('should return the client debt', async() => {
let result = await app.models.Client.getDebt(101); let result = await app.models.Client.getDebt(101);
expect(result.debt).toEqual(329.13); expect(result.debt).toEqual(-14.78);
}); });
}); });

View File

@ -17,7 +17,7 @@ describe('client summary()', () => {
it('should return a summary object containing debt', async() => { it('should return a summary object containing debt', async() => {
let result = await app.models.Client.summary(101); let result = await app.models.Client.summary(101);
expect(result.debt.debt).toEqual(329.13); expect(result.debt.debt).toEqual(-14.78);
}); });
it('should return a summary object containing averageInvoiced', async() => { it('should return a summary object containing averageInvoiced', async() => {

View File

@ -34,6 +34,7 @@ module.exports = Self => {
r.id, r.id,
r.isConciliate, r.isConciliate,
r.payed, r.payed,
r.workerFk,
c.code company, c.code company,
r.created, r.created,
r.invoiceFk ref, r.invoiceFk ref,
@ -41,7 +42,6 @@ module.exports = Self => {
r.amountPaid credit, r.amountPaid credit,
r.bankFk, r.bankFk,
u.nickname userNickname, u.nickname userNickname,
u.id userId,
r.clientFk, r.clientFk,
FALSE pdf, FALSE pdf,
FALSE isInvoice FALSE isInvoice

View File

@ -26,6 +26,12 @@
}, },
"isConciliate": { "isConciliate": {
"type": "date" "type": "date"
},
"description": {
"type": "string",
"mysql": {
"columnName": "invoiceFk"
}
} }
}, },
"relations": { "relations": {
@ -48,11 +54,6 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Bank", "model": "Bank",
"foreignKey": "bankFk" "foreignKey": "bankFk"
},
"invoice": {
"type": "belongsTo",
"model": "InvoiceOut",
"foreignKey": "invoiceFk"
} }
} }
} }

View File

@ -29,11 +29,31 @@ class Controller {
this.receipt.amountPaid = value; this.receipt.amountPaid = value;
} }
get amountPaid() {
return this.receipt.amountPaid;
}
set clientFk(value) {
this.receipt.clientFk = value;
}
get clientFk() {
return this.receipt.clientFk;
}
set companyFk(value) { set companyFk(value) {
this.receipt.companyFk = value; this.receipt.companyFk = value;
this.getAmountPaid(); this.getAmountPaid();
} }
set description(value) {
this.receipt.description = value;
}
get description() {
return this.receipt.description;
}
getAmountPaid() { getAmountPaid() {
let filter = { let filter = {
where: { where: {
@ -68,7 +88,7 @@ class Controller {
} }
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate']; Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
ngModule.component('vnClientRiskCreate', { ngModule.component('vnClientBalanceCreate', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {
@ -76,6 +96,8 @@ ngModule.component('vnClientRiskCreate', {
bankFk: '<?', bankFk: '<?',
amountPaid: '<?', amountPaid: '<?',
onResponse: '&?', onResponse: '&?',
companyFk: '<?' companyFk: '<?',
description: '<?',
clientFk: '<?'
} }
}); });

View File

@ -9,15 +9,14 @@
vn-id="riskModel" vn-id="riskModel"
url="/client/api/ClientRisks" url="/client/api/ClientRisks"
filter="$ctrl.filter" filter="$ctrl.filter"
data="$ctrl.riskTotal"> data="$ctrl.clientRisks">
</vn-crud-model> </vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-horizontal> <vn-horizontal>
<vn-one></vn-one>
<vn-one> <vn-one>
</vn-one> <vn-autocomplete
<vn-one>
<vn-autocomplete vn-one
vn-id="company" vn-id="company"
field="$ctrl.companyFk" field="$ctrl.companyFk"
on-change="$ctrl.setOrder(value)" on-change="$ctrl.setOrder(value)"
@ -28,9 +27,9 @@
</vn-autocomplete> </vn-autocomplete>
</vn-one> </vn-one>
<vn-one> <vn-one>
<div class="totalBox" ng-if="$ctrl.riskTotal.length"> <div class="totalBox" ng-if="$ctrl.clientRisks.length">
<h6 translate>Total by company</h6> <h6 translate>Total by company</h6>
<vn-auto ng-repeat="riskByCompany in $ctrl.riskTotal"> <vn-auto ng-repeat="riskByCompany in $ctrl.clientRisks">
<vn-label-value <vn-label-value
label="{{riskByCompany.company.code}}" label="{{riskByCompany.company.code}}"
value="{{riskByCompany.amount | currency: 'EUR':2}}"> value="{{riskByCompany.amount | currency: 'EUR':2}}">
@ -62,7 +61,7 @@
<vn-td> <vn-td>
<span <span
class="link" class="link"
ng-click="$ctrl.showWorkerDescriptor($event, balance.userId)"> ng-click="$ctrl.showWorkerDescriptor($event, balance.workerFk)">
{{::balance.userNickname}} {{::balance.userNickname}}
</span> </span>
</vn-td> </vn-td>
@ -89,7 +88,7 @@
href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.accessToken}}"> href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.accessToken}}">
<vn-icon-button <vn-icon-button
icon="cloud_download" icon="cloud_download"
title="Download PDF"> title="{{'Download PDF' | translate}}">
</vn-icon-button> </vn-icon-button>
</a> </a>
</vn-td> </vn-td>
@ -111,12 +110,12 @@
ng-click="$ctrl.openCreateDialog()"> ng-click="$ctrl.openCreateDialog()">
</vn-float-button> </vn-float-button>
<vn-client-risk-create vn-id="riskCreateDialog"> <vn-client-balance-create vn-id="balanceCreateDialog">
</vn-client-risk-create> </vn-client-balance-create>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor" vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker"> worker-fk="$ctrl.selectedWorker">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-invoice-out-descriptor-popover <vn-invoice-out-descriptor-popover

View File

@ -46,7 +46,7 @@ class Controller {
const params = {filter: this.filter}; const params = {filter: this.filter};
this.$http.get(`/client/api/ClientRisks`, {params}).then(response => { this.$http.get(`/client/api/ClientRisks`, {params}).then(response => {
if (response.data) { if (response.data) {
this.riskTotal = response.data; this.clientRisks = response.data;
this.getBalances(); this.getBalances();
} }
@ -59,7 +59,7 @@ class Controller {
getCurrentBalance() { getCurrentBalance() {
const selectedCompany = this.$.company.selection; const selectedCompany = this.$.company.selection;
const currentBalance = this.riskTotal.find(balance => { const currentBalance = this.clientRisks.find(balance => {
return balance.companyFk === selectedCompany.id; return balance.companyFk === selectedCompany.id;
}); });
@ -80,24 +80,20 @@ class Controller {
openCreateDialog() { openCreateDialog() {
this.$.riskCreateDialog.companyFk = this.companyFk; this.$.balanceCreateDialog.companyFk = this.companyFk;
this.$.riskCreateDialog.onResponse = () => { this.$.balanceCreateDialog.onResponse = () => {
this.refresh(); this.refresh();
}; };
this.$.riskCreateDialog.show(); this.$.balanceCreateDialog.show();
} }
onDownload() { showWorkerDescriptor(event, workerFk) {
alert('Not implemented yet');
}
showWorkerDescriptor(event, userId) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.selectedWorker = userId; this.selectedWorker = workerFk;
this.$.workerDescriptor.parent = event.target; this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show(); this.$.workerDescriptor.show();
} }
@ -117,7 +113,7 @@ class Controller {
Controller.$inject = ['$stateParams', '$translate', '$scope', 'vnToken', '$http']; Controller.$inject = ['$stateParams', '$translate', '$scope', 'vnToken', '$http'];
ngModule.component('vnClientRiskIndex', { ngModule.component('vnClientBalanceIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
}); });

View File

@ -1,7 +1,7 @@
import './index'; import './index';
describe('Client', () => { describe('Client', () => {
describe('Component vnClientRiskIndex', () => { describe('Component vnClientBalanceIndex', () => {
let $componentController; let $componentController;
let $scope; let $scope;
let $httpBackend; let $httpBackend;
@ -15,7 +15,7 @@ describe('Client', () => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_; $httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new(); $scope = $rootScope.$new();
controller = $componentController('vnClientRiskIndex', {$scope}); controller = $componentController('vnClientBalanceIndex', {$scope});
})); }));
describe('balances() setter', () => { describe('balances() setter', () => {

View File

@ -0,0 +1,10 @@
@import "./variables";
vn-client-balance-index {
.totalBox {
border: $border-thin-light;
text-align: left;
float: right
}
}

View File

@ -10,7 +10,7 @@
<vn-horizontal> <vn-horizontal>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
label="Pay method" label="Billing data"
vn-acl="salesAssistant" vn-acl="salesAssistant"
field="$ctrl.client.payMethodFk" field="$ctrl.client.payMethodFk"
url="/client/api/PayMethods" url="/client/api/PayMethods"

View File

@ -19,8 +19,8 @@ import './credit/index';
import './credit/create'; import './credit/create';
import './greuge/index'; import './greuge/index';
import './greuge/create'; import './greuge/create';
import './risk/index'; import './balance/index';
import './risk/create'; import './balance/create';
import './mandate'; import './mandate';
import './summary'; import './summary';
import './recovery/index'; import './recovery/index';

View File

@ -34,7 +34,7 @@ Search client by id or name: Buscar clientes por identificador o nombre
Clients: Clientes Clients: Clientes
New client: Nuevo cliente New client: Nuevo cliente
Fiscal data: Datos fiscales Fiscal data: Datos fiscales
Pay method: Forma de pago Billing data: Forma de pago
Addresses: Consignatarios Addresses: Consignatarios
New address: Nuevo consignatario New address: Nuevo consignatario
Edit address: Editar consignatario Edit address: Editar consignatario

View File

@ -12,7 +12,7 @@
{"state": "client.card.note.index", "icon": "insert_drive_file"}, {"state": "client.card.note.index", "icon": "insert_drive_file"},
{"state": "client.card.credit.index", "icon": "credit_card"}, {"state": "client.card.credit.index", "icon": "credit_card"},
{"state": "client.card.greuge.index", "icon": "work"}, {"state": "client.card.greuge.index", "icon": "work"},
{"state": "client.card.risk.index", "icon": "icon-invoices"}, {"state": "client.card.balance.index", "icon": "icon-invoices"},
{"state": "client.card.recovery.index", "icon": "icon-recovery"}, {"state": "client.card.recovery.index", "icon": "icon-recovery"},
{"state": "client.card.log", "icon": "history"}, {"state": "client.card.log", "icon": "history"},
{ {
@ -82,7 +82,7 @@
"url": "/billing-data", "url": "/billing-data",
"state": "client.card.billingData", "state": "client.card.billingData",
"component": "vn-client-billing-data", "component": "vn-client-billing-data",
"description": "Pay method", "description": "Billing data",
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }
@ -188,22 +188,22 @@
"component": "vn-client-mandate", "component": "vn-client-mandate",
"description": "Mandates" "description": "Mandates"
}, { }, {
"url": "/risk", "url": "/balance",
"abstract": true, "abstract": true,
"state": "client.card.risk", "state": "client.card.balance",
"component": "ui-view" "component": "ui-view"
}, { }, {
"url": "/index", "url": "/index",
"state": "client.card.risk.index", "state": "client.card.balance.index",
"component": "vn-client-risk-index", "component": "vn-client-balance-index",
"description": "Balance", "description": "Balance",
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }
}, { }, {
"url": "/create?payed&companyFk&bankFk&payedAmount", "url": "/create?payed&companyFk&bankFk&payedAmount",
"state": "client.card.risk.create", "state": "client.card.balance.create",
"component": "vn-client-risk-create", "component": "vn-client-balance-create",
"description": "New payment", "description": "New payment",
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"

View File

@ -0,0 +1,26 @@
module.exports = Self => {
Self.remoteMethod('book', {
description: 'Book a invoiceOut',
accessType: 'WRITE',
accepts: {
arg: 'ref',
type: 'string',
required: true,
description: 'The invoiceOut ref',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:ref/book',
verb: 'POST'
}
});
Self.book = async ref => {
return Self.rawSql(`CALL vn.invoiceOutAgain(?)`, [ref]);
};
};

View File

@ -0,0 +1,45 @@
module.exports = Self => {
Self.remoteMethod('delete', {
description: 'Delete a invoiceOut',
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'string',
required: true,
description: 'The invoiceOut id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/delete',
verb: 'POST'
}
});
Self.delete = async id => {
const transaction = await Self.beginTransaction({});
try {
let invoiceOut = await Self.findById(id);
let tickets = await Self.app.models.Ticket.find({where: {refFk: invoiceOut.ref}});
const promises = [];
tickets.forEach(ticket => {
promises.push(ticket.updateAttribute('refFk', null, {transaction}));
});
return Promise.all(promises).then(async() => {
await invoiceOut.destroy({transaction});
await transaction.commit();
return tickets;
});
} catch (e) {
await transaction.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,128 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethodCtx('filter', {
description: 'Find all instances of the model matched by filter from the data source.',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
},
{
arg: 'search',
type: 'String',
description: 'Searchs the invoiceOut by id',
http: {source: 'query'}
}, {
arg: 'clientFk',
type: 'Integer',
description: 'The client id',
http: {source: 'query'}
}, {
arg: 'hasPdf',
type: 'Boolean',
description: 'Whether the the invoiceOut has PDF or not',
http: {source: 'query'}
}, {
arg: 'amount',
type: 'Number',
description: 'The amount filter',
http: {source: 'query'}
}, {
arg: 'min',
type: 'Number',
description: 'The minimun amount flter',
http: {source: 'query'}
}, {
arg: 'max',
type: 'Number',
description: 'The maximun amount flter',
http: {source: 'query'}
}, {
arg: 'issued',
type: 'Date',
description: 'The issued date filter',
http: {source: 'query'}
}, {
arg: 'created',
type: 'Date',
description: 'The created date filter',
http: {source: 'query'}
}, {
arg: 'dued',
type: 'Date',
description: 'The due date filter',
http: {source: 'query'}
}
],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/filter`,
verb: 'GET'
}
});
Self.filter = async(ctx, filter) => {
let conn = Self.dataSource.connector;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return {ref: {like: `%${value}%`}};
case 'min':
return {amount: {gte: value}};
case 'max':
return {amount: {lte: value}};
case 'hasPdf':
return {'i.pdf': value};
case 'created':
return {'i.created': value};
case 'amount':
case 'clientFk':
case 'companyFk':
case 'issued':
case 'dued':
return {[param]: value};
}
});
filter = mergeFilters(ctx.args.filter, {where});
let stmts = [];
let stmt;
stmt = new ParameterizedSQL(
`SELECT
i.id,
i.ref,
i.issued,
i.amount,
i.created,
i.dued,
i.clientFk,
i.pdf AS hasPdf,
c.socialName AS clientSocialName,
co.code AS companyCode
FROM invoiceOut i
LEFT JOIN client c ON c.id = i.clientFk
LEFT JOIN company co ON co.id = i.companyFk`
);
stmt.merge(conn.makeSuffix(filter));
let itemsIndex = stmts.push(stmt) - 1;
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
return itemsIndex === 0 ? result : result[itemsIndex];
};
};

View File

@ -0,0 +1,49 @@
module.exports = Self => {
Self.remoteMethodCtx('regenerate', {
description: 'Sends an invoice to a regeneration queue',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The invoiceOut id',
http: {source: 'path'}
}],
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/regenerate',
verb: 'POST'
}
});
Self.regenerate = async(ctx, id) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const invoiceReportFk = 30; // FIXME - Should be deprecated
const worker = await models.Worker.findOne({where: {userFk: userId}});
const transaction = await Self.beginTransaction({});
try {
// Remove all invoice references from tickets
const invoiceOut = await models.InvoiceOut.findById(id, {transaction});
await invoiceOut.updateAttributes({
hasPdf: false
});
// Send to print queue
await Self.rawSql(`
INSERT INTO vn.printServerQueue (reportFk, param1, workerFk)
VALUES (?, ?, ?)`, [invoiceReportFk, id, worker.id], {transaction});
await transaction.commit();
return invoiceOut;
} catch (e) {
await transaction.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,32 @@
const app = require('vn-loopback/server/server');
describe('invoiceOut book()', () => {
const invoiceOutId = 5;
let invoiceOutRef;
let OriginalInvoiceOut;
let updatedInvoiceOut;
afterAll(async done => {
updatedInvoiceOut.updateAttributes({booked: OriginalInvoiceOut.booked, hasPdf: OriginalInvoiceOut.hasPdf});
done();
});
it('should check that invoice out booked is untainted', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
expect(invoiceOut.booked).toBeNull();
expect(invoiceOut.hasPdf).toBeTruthy();
});
it(`should confirm the book property have been updated`, async() => {
OriginalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
invoiceOutRef = OriginalInvoiceOut.ref;
await app.models.InvoiceOut.book(invoiceOutRef);
updatedInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
expect(updatedInvoiceOut.booked).toEqual(jasmine.any(Object));
expect(updatedInvoiceOut.hasPdf).toBeFalsy();
});
});

View File

@ -0,0 +1,38 @@
const app = require('vn-loopback/server/server');
describe('invoiceOut delete()', () => {
const invoiceOutId = 2;
let OriginalInvoiceOut;
let OriginalTickets;
afterAll(async done => {
const newInvoiceOut = await app.models.InvoiceOut.create(OriginalInvoiceOut);
await newInvoiceOut.updateAttribute('ref', OriginalInvoiceOut.ref);
const promises = [];
OriginalTickets.forEach(ticket => {
promises.push(ticket.updateAttribute('refFk', newInvoiceOut.ref));
});
Promise.all(promises);
done();
});
it('should check that there is two tickets from the invoice id 2', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
const tickets = await app.models.Ticket.find({where: {refFk: invoiceOut.ref}});
expect(tickets.length).toEqual(2);
expect(tickets[0].id).toEqual(2);
});
it(`should check the two tickets from the invoice id 2 that are not invoiced`, async() => {
OriginalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
await app.models.InvoiceOut.delete(invoiceOutId);
OriginalTickets = await app.models.Ticket.find({where: {id: {inq: [2, 3]}}});
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
expect(OriginalTickets[0].refFk).toBeNull();
expect(OriginalTickets[1].refFk).toBeNull();
expect(invoiceOut).toBeNull();
});
});

View File

@ -0,0 +1,39 @@
const app = require('vn-loopback/server/server');
describe('invoiceOut regenerate()', () => {
const invoiceReportFk = 30; // FIXME - Should be deprecated
const invoiceOutId = 1;
afterAll(async done => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
await invoiceOut.updateAttributes({hasPdf: true});
await app.models.InvoiceOut.rawSql(`
DELETE FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
done();
});
it('should check that the invoice has a PDF and is not in print generation queue', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
const [queue] = await app.models.InvoiceOut.rawSql(`
SELECT COUNT(*) AS total
FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
expect(invoiceOut.hasPdf).toBeTruthy();
expect(queue.total).toEqual(0);
});
it(`should mark the invoice as doesn't have PDF and add it to a print queue`, async() => {
const ctx = {req: {accessToken: {userId: 5}}};
const invoiceOut = await app.models.InvoiceOut.regenerate(ctx, invoiceOutId);
const [queue] = await app.models.InvoiceOut.rawSql(`
SELECT COUNT(*) AS total
FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
expect(invoiceOut.hasPdf).toBeFalsy();
expect(queue.total).toEqual(1);
});
});

View File

@ -81,14 +81,11 @@ module.exports = Self => {
let ticketTotalsIndex = stmts.push('SELECT * FROM tmp.ticketTotal') - 1; let ticketTotalsIndex = stmts.push('SELECT * FROM tmp.ticketTotal') - 1;
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`
SELECT tc.description as type, SUM(ROUND(s.quantity * s.price * (100 - s.discount) / 100,2)) AS base SELECT iot.* , pgc.*, IF(pe.equFk IS NULL, taxableBase, 0) AS Base, pgc.rate / 100 as vatPercent
FROM vn.sale s FROM vn.invoiceOutTax iot
JOIN vn.ticket t ON t.id = s.ticketFk JOIN vn.pgc ON pgc.code = iot.pgcFk
LEFT JOIN vn.itemTaxCountry itc ON itc.itemFk = s.itemFk LEFT JOIN vn.pgcEqu pe ON pe.equFk = pgc.code
JOIN vn.country c ON c.id = itc.countryFK AND c.id = ? WHERE invoiceOutFk = ?`, [summary.invoiceOut.id]);
LEFT JOIN vn.taxClass tc ON tc.id = itc.taxClassFk
WHERE t.refFk = ?
GROUP BY type`, [summary.invoiceOut.supplier().countryFk, summary.invoiceOut.ref]);
let invoiceOutTaxesIndex = stmts.push(stmt) - 1; let invoiceOutTaxesIndex = stmts.push(stmt) - 1;
stmts.push( stmts.push(

View File

@ -1,4 +1,8 @@
module.exports = Self => { module.exports = Self => {
require('../methods/invoiceOut/filter')(Self);
require('../methods/invoiceOut/summary')(Self); require('../methods/invoiceOut/summary')(Self);
require('../methods/invoiceOut/download')(Self); require('../methods/invoiceOut/download')(Self);
require('../methods/invoiceOut/regenerate')(Self);
require('../methods/invoiceOut/delete')(Self);
require('../methods/invoiceOut/book')(Self);
}; };

View File

@ -35,7 +35,7 @@
"type": "date" "type": "date"
}, },
"hasPdf": { "hasPdf": {
"type": "Number", "type": "Boolean",
"mysql": { "mysql": {
"columnName": "pdf" "columnName": "pdf"
} }

View File

@ -6,6 +6,16 @@
<a translate-attr="{title: 'Preview'}" ui-sref="invoiceOut.card.summary({id: $ctrl.invoiceOut.id})"> <a translate-attr="{title: 'Preview'}" ui-sref="invoiceOut.card.summary({id: $ctrl.invoiceOut.id})">
<vn-icon icon="desktop_windows"></vn-icon> <vn-icon icon="desktop_windows"></vn-icon>
</a> </a>
<vn-icon-menu
vn-id="more-button"
icon="more_vert"
show-filter="false"
value-field="callback"
translate-fields="['name']"
data="$ctrl.moreOptions"
on-change="$ctrl.onMoreChange(value)"
on-open="$ctrl.onMoreOpen()">
</vn-icon-menu>
</div> </div>
<div class="body"> <div class="body">
<div class="attributes"> <div class="attributes">
@ -40,14 +50,16 @@
icon="{{::$ctrl.quicklinks.btnTwo.icon}}"> icon="{{::$ctrl.quicklinks.btnTwo.icon}}">
</vn-icon> </vn-icon>
</a> </a>
<a ng-if="$ctrl.quicklinks.btnThree"
vn-tooltip="{{::$ctrl.quicklinks.btnThree.tooltip}}"
ui-sref="{{::$ctrl.quicklinks.btnThree.state}}" target="_blank">
<vn-icon
class="mdl-button mdl-js-button mdl-button--colored"
icon="{{::$ctrl.quicklinks.btnThree.icon}}">
</vn-icon>
</a>
</div> </div>
</div> </div>
</div> </div>
<vn-confirm
vn-id="deleteConfirmation"
on-response="$ctrl.deleteInvoiceOut(response)"
question="Are you sure you want to delete this invoice?">
</vn-confirm>
<vn-confirm
vn-id="bookConfirmation"
on-response="$ctrl.bookInvoiceOut(response)"
question="Are you sure you want to book this invoice?">
</vn-confirm>

View File

@ -1,6 +1,34 @@
import ngModule from '../module'; import ngModule from '../module';
class Controller { class Controller {
constructor($scope, vnToken, vnApp, $state, $translate, $http, aclService) {
this.$scope = $scope;
this.accessToken = vnToken.token;
this.vnApp = vnApp;
this.$state = $state;
this.$translate = $translate;
this.$http = $http;
this.aclService = aclService;
this.moreOptions = [
{callback: this.showInvoiceOutPdf, name: 'Show invoice PDF'},
{callback: this.showDeleteInvoiceOutDialog, name: 'Delete Invoice', acl: 'invoicing'},
{callback: this.showBookInvoiceOutDialog, name: 'Book invoice', acl: 'invoicing'}
];
}
onMoreOpen() {
let options = this.moreOptions.filter(option => {
const hasAclProperty = Object.hasOwnProperty.call(option, 'acl');
return !hasAclProperty || (hasAclProperty && this.aclService.hasAny([option.acl]));
});
this.$scope.moreButton.data = options;
}
onMoreChange(callback) {
callback.call(this);
}
set invoiceOut(value) { set invoiceOut(value) {
this._invoiceOut = value; this._invoiceOut = value;
@ -24,6 +52,39 @@ class Controller {
return this._invoiceOut; return this._invoiceOut;
} }
showInvoiceOutPdf() {
let url = `api/InvoiceOuts/${this.invoiceOut.id}/download?access_token=${this.accessToken}`;
window.open(url, '_blank');
}
showDeleteInvoiceOutDialog() {
this.$scope.deleteConfirmation.show();
}
showBookInvoiceOutDialog() {
this.$scope.bookConfirmation.show();
}
deleteInvoiceOut(response) {
if (response === 'ACCEPT') {
const query = `/invoiceOut/api/InvoiceOuts/${this.invoiceOut.id}/delete`;
this.$http.post(query).then(() => {
this.vnApp.showSuccess(this.$translate.instant('InvoiceOut deleted'));
this.$state.go('invoiceOut.index');
});
}
}
bookInvoiceOut(response) {
if (response === 'ACCEPT') {
const query = `/invoiceOut/api/InvoiceOuts/${this.invoiceOut.ref}/book`;
this.$http.post(query).then(() => {
this.vnApp.showSuccess(this.$translate.instant('InvoiceOut booked'));
this.$state.reload();
});
}
}
set quicklinks(value = {}) { set quicklinks(value = {}) {
this._quicklinks = Object.assign(value, this._quicklinks); this._quicklinks = Object.assign(value, this._quicklinks);
} }
@ -33,7 +94,7 @@ class Controller {
} }
} }
Controller.$inject = ['$http', '$state']; Controller.$inject = ['$scope', 'vnToken', 'vnApp', '$state', '$translate', '$http', 'aclService'];
ngModule.component('vnInvoiceOutDescriptor', { ngModule.component('vnInvoiceOutDescriptor', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -2,3 +2,10 @@ Volume exceded: Volumen excedido
Volume: Volumen Volume: Volumen
Client card: Ficha del cliente Client card: Ficha del cliente
Invoice ticket list: Listado de tickets de la factura Invoice ticket list: Listado de tickets de la factura
Show invoice PDF: Ver factura en PDF
Delete Invoice: Borrar factura
InvoiceOut deleted: Factura eliminada
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
Book invoice: Asentar factura
InvoiceOut booked: Factura asentada
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?

View File

@ -1,20 +1,16 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="/api/InvoiceOuts" url="/api/InvoiceOuts/filter"
filter="::$ctrl.filter"
limit="20" limit="20"
data="invoicesOut" data="invoiceOuts"
order="issued DESC" order="issued DESC">
auto-load="false">
</vn-crud-model> </vn-crud-model>
<div class="content-block"> <div class="content-block">
<div class="vn-list"> <div class="vn-list">
<vn-card pad-medium-h> <vn-card pad-medium-h>
<vn-searchbar <vn-searchbar
panel="vn-invoice-search-panel" panel="vn-invoice-search-panel"
model="model" on-search="$ctrl.onSearch($params)"
expr-builder="$ctrl.exprBuilder(param, value)"
auto-load="true"
info="Search invoices by reference" info="Search invoices by reference"
vn-focus> vn-focus>
</vn-searchbar> </vn-searchbar>
@ -36,7 +32,7 @@
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>
<a ng-repeat="invoiceOut in invoicesOut" <a ng-repeat="invoiceOut in invoiceOuts"
class="clickable vn-tr searchResult" class="clickable vn-tr searchResult"
ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})"> ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})">
<vn-td>{{::invoiceOut.ref | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceOut.ref | dashIfEmpty}}</vn-td>
@ -46,11 +42,11 @@
<span <span
class="link" class="link"
ng-click="$ctrl.showClientDescriptor($event, invoiceOut.clientFk)"> ng-click="$ctrl.showClientDescriptor($event, invoiceOut.clientFk)">
{{::invoiceOut.client.name | dashIfEmpty}} {{::invoiceOut.clientSocialName | dashIfEmpty}}
</span> </span>
</vn-td> </vn-td>
<vn-td>{{::invoiceOut.created | dateTime:'dd/MM/yyyy' | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceOut.created | dateTime:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td>{{::invoiceOut.company.code | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceOut.companyCode | dashIfEmpty}}</vn-td>
<vn-td>{{::invoiceOut.dued | dateTime:'dd/MM/yyyy' | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceOut.dued | dateTime:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td> <vn-td>
<vn-icon-button <vn-icon-button

View File

@ -5,42 +5,6 @@ export default class Controller {
this.accessToken = vnToken.token; this.accessToken = vnToken.token;
this.$ = $scope; this.$ = $scope;
this.selectedInvoiceOut = null; this.selectedInvoiceOut = null;
this.filter = {
include: [
{
relation: 'client',
scope: {
fields: ['name']
}
},
{
relation: 'company',
scope: {
fields: ['code']
}
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return {ref: {like: `%${value}%`}};
case 'min':
return {amount: {gte: value}};
case 'max':
return {amount: {lte: value}};
case 'hasPdf':
case 'amount':
case 'clientFk':
case 'companyFk':
case 'issued':
case 'created':
case 'dued':
return {[param]: value};
}
} }
showClientDescriptor(event, clientFk) { showClientDescriptor(event, clientFk) {
@ -68,6 +32,13 @@ export default class Controller {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
} }
onSearch(params) {
if (params)
this.$.model.applyFilter(null, params);
else
this.$.model.clear();
}
} }
Controller.$inject = ['$scope', 'vnToken']; Controller.$inject = ['$scope', 'vnToken'];

View File

@ -14,25 +14,31 @@
<vn-label-value label="Booked" <vn-label-value label="Booked"
value="{{$ctrl.summary.invoiceOut.booked | dateTime: 'dd/MM/yyyy'}}"> value="{{$ctrl.summary.invoiceOut.booked | dateTime: 'dd/MM/yyyy'}}">
</vn-label-value> </vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Company" <vn-label-value label="Company"
value="{{$ctrl.summary.invoiceOut.company.code | dashIfEmpty}}"> value="{{$ctrl.summary.invoiceOut.company.code | dashIfEmpty}}">
</vn-label-value> </vn-label-value>
<vn-icon-button
ng-show="$ctrl.summary.invoiceOut.hasPdf"
ng-click="$ctrl.openPdf(invoiceOut.id, $event)"
icon="cloud_download"
title="Download PDF"
vn-tooltip="Download PDF">
</vn-icon-button>
</vn-one>
<vn-one>
<vn-label-value ng-repeat="tax in $ctrl.summary.invoiceOut.taxesBreakdown"
label="{{tax.type}}"
value="{{tax.base | currency: 'EUR': 2}}">
</vn-label-value>
</vn-one> </vn-one>
<vn-two>
<h4 translate>Desglose impositivo</h4>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Type</vn-th>
<vn-th>Taxable base</vn-th>
<vn-th>Rate</vn-th>
<vn-th>Fee</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="tax in $ctrl.summary.invoiceOut.taxesBreakdown">
<vn-td>{{tax.name}}</vn-td>
<vn-td>{{tax.taxableBase | currency: 'EUR': 2}}</vn-td>
<vn-td>{{tax.rate}}%</vn-td>
<vn-td>{{tax.vat | currency: 'EUR': 2}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-two>
<vn-auto> <vn-auto>
<h4 translate>Ticket</h4> <h4 translate>Ticket</h4>
<vn-table model="model"> <vn-table model="model">
@ -40,6 +46,7 @@
<vn-tr> <vn-tr>
<vn-th number>Ticket id</vn-th> <vn-th number>Ticket id</vn-th>
<vn-th>Alias</vn-th> <vn-th>Alias</vn-th>
<vn-th>Shipped</vn-th>
<vn-th number>Amount</vn-th> <vn-th number>Amount</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
@ -59,6 +66,7 @@
{{ticket.nickname}} {{ticket.nickname}}
</span> </span>
</vn-td> </vn-td>
<vn-td>{{ticket.shipped | dateTime: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{ticket.total | currency: 'EUR': 2}}</vn-td> <vn-td number>{{ticket.total | currency: 'EUR': 2}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>

View File

@ -18,13 +18,6 @@ class Controller {
return this._invoiceOut; return this._invoiceOut;
} }
openPdf(id, event) {
let url = `api/InvoiceOuts/${id}/download?access_token=${this.accessToken}`;
window.open(url, '_blank');
event.preventDefault();
event.stopImmediatePropagation();
}
getSummary() { getSummary() {
this.$http.get(`/api/InvoiceOuts/${this.invoiceOut.id}/summary`).then(response => { this.$http.get(`/api/InvoiceOuts/${this.invoiceOut.id}/summary`).then(response => {
this.summary = response.data; this.summary = response.data;
@ -36,7 +29,6 @@ class Controller {
this.$.clientDescriptor.parent = event.target; this.$.clientDescriptor.parent = event.target;
this.$.clientDescriptor.show(); this.$.clientDescriptor.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation();
} }
showTicketDescriptor(event, ticketFk) { showTicketDescriptor(event, ticketFk) {
@ -44,14 +36,12 @@ class Controller {
this.$.ticketDescriptor.parent = event.target; this.$.ticketDescriptor.parent = event.target;
this.$.ticketDescriptor.show(); this.$.ticketDescriptor.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation();
} }
preview(event, invoiceOut) { preview(event, invoiceOut) {
this.selectedInvoiceOut = invoiceOut; this.selectedInvoiceOut = invoiceOut;
this.$.invoiceOutSummaryDialog.show(); this.$.invoiceOutSummaryDialog.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation();
} }
} }

View File

@ -1,40 +1,26 @@
import './index.js'; import './index.js';
describe('Route', () => { describe('InvoiceOut', () => {
describe('Component summary', () => { describe('Component summary', () => {
let controller; let controller;
let $httpBackend; let $httpBackend;
beforeEach(ngModule('route')); beforeEach(ngModule('invoiceOut'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => { beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
controller = $componentController('vnRouteSummary'); controller = $componentController('vnInvoiceOutSummary');
controller.route = {id: 1}; controller.invoiceOut = {id: 1};
})); }));
describe('getSummary()', () => { describe('getSummary()', () => {
it('should perform a query to set summary', () => { it('should perform a query to set summary', () => {
$httpBackend.when('GET', `/api/Routes/1/summary`).respond(200, 24); $httpBackend.when('GET', `/api/InvoiceOuts/1/summary`).respond(200, 'the data you are looking for');
$httpBackend.expect('GET', `/api/Routes/1/summary`); $httpBackend.expect('GET', `/api/InvoiceOuts/1/summary`);
controller.getSummary(); controller.getSummary();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.summary).toEqual(24); expect(controller.summary).toEqual('the data you are looking for');
});
});
describe('sumPackages()', () => {
it('should calculate the packages total', () => {
controller.summary = {
tickets: [
{packages: 3},
{packages: 1}
]
};
controller.sumPackages();
expect(controller.packagesTotal).toEqual(4);
}); });
}); });
}); });

Some files were not shown because too many files have changed in this diff Show More