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]
indent: [error, 4]
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": {
"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`)
VALUES
( 1, 'T', 500 , DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 101, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1),
( 2, 'T', 350.50 , DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 102, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1),
( 3, 'T', 90.30 , CURDATE(), 103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1),
( 4, 'T', 290.30 , DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1),
( 5, 'A', 190.30 , DATE_ADD(CURDATE(), INTERVAL +2 MONTH), 103, 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', 208.35, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 102, CURDATE(), 442, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), CURDATE(), 1, 1),
( 3, 'T', 20.02, CURDATE(), 103, CURDATE(), 442, CURDATE(), null, 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', 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 = 'T2222222' WHERE id = 2;
@ -413,6 +413,19 @@ INSERT INTO `vn`.`invoiceOutTax` (`invoiceOutFk`, `taxableBase`, `vat`, `pgcFk`)
(5, 100, 10, 4722000010),
(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`)
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) ),
@ -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) ),
(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) ),
(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) ),
(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) ),
(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, 'A1111111', 0, DATE_ADD(CURDATE(), INTERVAL -3 MONTH) ),
(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) ),
(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`)
VALUES
(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)),
(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)),
(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()),
(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()),
(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)),
(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)),
(17, 2, 22, 'Melee weapon combat first 15cm', 30, 2.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 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)),
(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)),
@ -816,7 +829,7 @@ INSERT INTO `vn`.`itemTag`(`id`,`itemFk`,`tagFk`,`value`,`priority`)
(6 , 1, 67, 'precission', 6),
(7 , 1, 23, '1', 7),
(8 , 2, 56, 'Melee weapon', 1),
(9 , 2, 58, 'combat first', 2),
(9 , 2, 58, 'combat fist', 2),
(10, 2, 27, '15cm', 3),
(11, 2, 36, 'Stark Industries', 4),
(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)),
(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)),
(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`)
VALUES
(1, 'delivery charge', 1, 2.00, 1, 1),
(2, 'training course', 1, 10.00, 1, 2),
(3, 'delivery charge', 1, 5.50, 1, 11);
(1, 'Documentos', 1, 2.00, 1, 1),
(2, 'Porte Agencia', 1, 10.00, 1, 2),
(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`)
@ -1260,7 +1275,7 @@ INSERT INTO `pbx`.`sip`(`user_id`, `extension`, `secret`, `caller_id`)
(9, 1201, '123456', 'developer');
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`
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`;
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`;
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:
- /containers/salix:/etc/salix
- /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 {
vnTextfield: 'vn-textfield > 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 {
globalItems: {
applicationsMenuButton: `#apps`,
applicationsMenuVisible: `vn-main-menu [vn-id="apps-menu"] ul`,
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"]`,
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"]`,
applicationsMenuButton: '#apps',
applicationsMenuVisible: 'vn-main-menu [vn-id="apps-menu"] ul',
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"]',
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"]',
returnToModuleIndexButton: `a[translate-attr="{title: 'Return to module index'}"]`,
userMenuButton: `vn-topbar #user`,
userLocalWarehouse: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localWarehouseFk"]`,
userLocalBank: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localBankFk"]`,
userLocalCompany: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.localCompanyFk"]`,
userWarehouse: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.warehouseFk"]`,
userCompany: `vn-topbar vn-popover vn-autocomplete[field="$ctrl.companyFk"]`,
userMenuButton: 'vn-topbar #user',
userLocalWarehouse: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localWarehouseFk"]',
userLocalBank: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localBankFk"]',
userLocalCompany: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.localCompanyFk"]',
userWarehouse: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.warehouseFk"]',
userCompany: 'vn-topbar vn-popover vn-autocomplete[field="$ctrl.companyFk"]',
userConfigFirstAutocompleteClear: '#localWarehouse > div > div > div > vn-icon.clear',
userConfigSecondAutocompleteClear: '#localBank > 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: {
searchClientInput: `${components.vnTextfield}`,
searchButton: `vn-searchbar vn-icon[icon="search"]`,
searchResult: `vn-client-index .vn-list-item`,
searchButton: 'vn-searchbar vn-icon[icon="search"]',
searchResult: 'vn-client-index .vn-list-item',
createClientButton: `${components.vnFloatButton}`,
othersButton: `vn-left-menu li[name="Others"] > a`
othersButton: 'vn-left-menu li[name="Others"] > a'
},
createClientView: {
name: `${components.vnTextfield}[name="name"]`,
@ -37,53 +37,53 @@ export default {
email: `${components.vnTextfield}[name="email"]`,
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
createButton: `${components.vnSubmit}`,
cancelButton: `vn-button[href="#!/client/index"]`
cancelButton: 'vn-button[href="#!/client/index"]'
},
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'
},
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"]`,
contactInput: `${components.vnTextfield}[name="contact"]`,
phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`,
faxInput: `${components.vnTextfield}[name="fax"]`,
emailInput: `${components.vnTextfield}[name="email"]`,
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
channelAutocomplete: `vn-autocomplete[field="$ctrl.client.contactChannelFk"]`,
salesPersonAutocomplete: 'vn-autocomplete[field="$ctrl.client.salesPersonFk"]',
channelAutocomplete: 'vn-autocomplete[field="$ctrl.client.contactChannelFk"]',
saveButton: `${components.vnSubmit}`
},
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"]`,
fiscalIdInput: `${components.vnTextfield}[name="fi"]`,
equalizationTaxCheckbox: `vn-check[label='Is equalizated'] md-checkbox`,
acceptPropagationButton: `vn-client-fiscal-data > vn-confirm button[response=ACCEPT]`,
equalizationTaxCheckbox: 'vn-check[label="Is equalizated"] md-checkbox',
acceptPropagationButton: 'vn-client-fiscal-data > vn-confirm button[response=ACCEPT]',
addressInput: `${components.vnTextfield}[name="street"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
postcodeInput: `${components.vnTextfield}[name="postcode"]`,
provinceAutocomplete: `vn-autocomplete[field="$ctrl.client.provinceFk"]`,
countryAutocomplete: `vn-autocomplete[field="$ctrl.client.countryFk"]`,
activeCheckbox: `vn-check[label="Active"] md-checkbox`,
frozenCheckbox: `vn-check[label="Frozen"] md-checkbox`,
invoiceByAddressCheckbox: `vn-check[label='Invoice by address'] md-checkbox`,
verifiedDataCheckbox: `vn-check[label="Verified data"] md-checkbox`,
hasToInvoiceCheckbox: `vn-check[label='Has to invoice'] md-checkbox`,
invoiceByMailCheckbox: `vn-check[label='Invoice by mail'] md-checkbox`,
viesCheckbox: `vn-check[label='Vies'] md-checkbox`,
provinceAutocomplete: 'vn-autocomplete[field="$ctrl.client.provinceFk"]',
countryAutocomplete: 'vn-autocomplete[field="$ctrl.client.countryFk"]',
activeCheckbox: 'vn-check[label="Active"] md-checkbox',
frozenCheckbox: 'vn-check[label="Frozen"] md-checkbox',
invoiceByAddressCheckbox: 'vn-check[label="Invoice by address"] md-checkbox',
verifiedDataCheckbox: 'vn-check[label="Verified data"] md-checkbox',
hasToInvoiceCheckbox: 'vn-check[label="Has to invoice"] md-checkbox',
invoiceByMailCheckbox: 'vn-check[label="Invoice by mail"] md-checkbox',
viesCheckbox: 'vn-check[label="Vies"] md-checkbox',
saveButton: `${components.vnSubmit}`
},
clientPayMethod: {
payMethodAutocomplete: `vn-autocomplete[field="$ctrl.client.payMethodFk"]`,
clientBillingData: {
payMethodAutocomplete: 'vn-autocomplete[field="$ctrl.client.payMethodFk"]',
IBANInput: `${components.vnTextfield}[name="iban"]`,
dueDayInput: `${components.vnInputNumber}[name="dueDay"]`,
receivedCoreLCRCheckbox: `vn-check[label='Received LCR'] md-checkbox`,
receivedCoreVNLCheckbox: `vn-check[label='Received core VNL'] md-checkbox`,
receivedB2BVNLCheckbox: `vn-check[label='Received B2B VNL'] md-checkbox`,
receivedCoreLCRCheckbox: 'vn-check[label="Received LCR"] md-checkbox',
receivedCoreVNLCheckbox: 'vn-check[label="Received core VNL"] md-checkbox',
receivedB2BVNLCheckbox: 'vn-check[label="Received B2B VNL"] md-checkbox',
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',
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',
@ -92,41 +92,41 @@ export default {
saveButton: `${components.vnSubmit}`
},
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}`,
defaultCheckboxInput: `vn-check[label='Default'] md-checkbox`,
defaultCheckboxInput: 'vn-check[label="Default"] md-checkbox',
consigneeInput: `${components.vnTextfield}[name="nickname"]`,
streetAddressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
provinceAutocomplete: `vn-autocomplete[field="$ctrl.address.provinceFk"]`,
agencyAutocomplete: `vn-autocomplete[field="$ctrl.address.agencyModeFk"]`,
provinceAutocomplete: 'vn-autocomplete[field="$ctrl.address.provinceFk"]',
agencyAutocomplete: 'vn-autocomplete[field="$ctrl.address.agencyModeFk"]',
phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`,
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"]',
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']`,
activeCheckbox: `vn-check[label='Enabled'] 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"]`,
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"]`,
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"]`,
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"]',
activeCheckbox: 'vn-check[label="Enabled"] 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"]',
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"]',
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"]',
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'
},
clientWebAccess: {
webAccessButton: `vn-left-menu a[ui-sref="client.card.webAccess"]`,
enableWebAccessCheckbox: `vn-check[label='Enable web access'] md-checkbox`,
webAccessButton: 'vn-left-menu a[ui-sref="client.card.webAccess"]',
enableWebAccessCheckbox: 'vn-check[label="Enable web access"] md-checkbox',
userNameInput: `${components.vnTextfield}[name="name"]`,
saveButton: `${components.vnSubmit}`
},
clientNotes: {
addNoteFloatButton: `${components.vnFloatButton}`,
noteInput: `vn-textarea[label="Note"]`,
noteInput: 'vn-textarea[label="Note"]',
saveButton: `${components.vnSubmit}`,
firstNoteText: 'vn-client-note .text'
},
@ -140,7 +140,7 @@ export default {
addGreugeFloatButton: `${components.vnFloatButton}`,
amountInput: `${components.vnInputNumber}[name="amount"]`,
descriptionInput: `${components.vnTextfield}[name="description"]`,
typeAutocomplete: `vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]`,
typeAutocomplete: 'vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]',
saveButton: `${components.vnSubmit}`,
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'
},
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)',
lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before',
lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after'
},
clientRisk: {
riskButton: `vn-left-menu a[ui-sref="client.card.risk.index"]`,
companyAutocomplete: 'vn-client-risk-index vn-autocomplete[field="$ctrl.companyFk"]',
clientBalance: {
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
companyAutocomplete: 'vn-client-balance-index vn-autocomplete[field="$ctrl.companyFk"]',
newPaymentButton: `${components.vnFloatButton}`,
newPaymentBankInut: `vn-client-risk-create vn-textfield[field="$ctrl.receipt.bankFk"] input`,
newPaymentAmountInput: `vn-client-risk-create vn-input-number[field="$ctrl.receipt.amountPaid"] input`,
saveButton: `vn-client-risk-create vn-button[label="Save"]`,
firstRiskLineBalance: 'vn-client-risk-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
newPaymentBankInut: 'vn-client-balance-create vn-textfield[field="$ctrl.receipt.bankFk"] input',
newPaymentAmountInput: 'vn-client-balance-create vn-input-number[field="$ctrl.receipt.amountPaid"] input',
saveButton: 'vn-client-balance-create vn-button[label="Save"]',
firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
},
webPayment: {
@ -172,15 +172,15 @@ export default {
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"][aria-hidden="false"]'
},
itemsIndex: {
searchIcon: `vn-item-index vn-searchbar vn-icon[icon="search"]`,
goBackToModuleIndexButton: `vn-item-descriptor a[href="#!/item/index"]`,
searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]',
goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]',
createItemButton: `${components.vnFloatButton}`,
searchResult: `vn-item-index a.vn-tr`,
searchResultPreviewButton: `vn-item-index .buttons > [icon="desktop_windows"]`,
searchResultCloneButton: `vn-item-index .buttons > [icon="icon-clone"]`,
acceptClonationAlertButton: `vn-item-index [vn-id="clone"] [response="ACCEPT"]`,
searchItemInput: `vn-searchbar vn-textfield input`,
searchButton: `vn-searchbar vn-icon[icon="search"]`,
searchResult: 'vn-item-index a.vn-tr',
searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]',
searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
acceptClonationAlertButton: 'vn-item-index [vn-id="clone"] [response="ACCEPT"]',
searchItemInput: 'vn-searchbar vn-textfield input',
searchButton: 'vn-searchbar vn-icon[icon="search"]',
closeItemSummaryPreview: 'vn-item-index [vn-id="preview"] button.close',
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',
@ -201,90 +201,90 @@ export default {
},
itemCreateView: {
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
typeAutocomplete: `vn-autocomplete[field="$ctrl.item.typeFk"]`,
intrastatAutocomplete: `vn-autocomplete[field="$ctrl.item.intrastatFk"]`,
originAutocomplete: `vn-autocomplete[field="$ctrl.item.originFk"]`,
typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
createButton: `${components.vnSubmit}`,
cancelButton: `button[ui-sref="item.index"]`
cancelButton: 'button[ui-sref="item.index"]'
},
itemDescriptor: {
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)`,
regularizeQuantityInput: `vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input`,
moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon',
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',
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]',
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"]',
navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]'
},
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"]',
typeAutocomplete: `vn-autocomplete[field="$ctrl.item.typeFk"]`,
intrastatAutocomplete: `vn-autocomplete[field="$ctrl.item.intrastatFk"]`,
nameInput: `vn-textfield[label="Name"] input`,
relevancyInput: `vn-input-number[label="Relevancy"] input`,
originAutocomplete: `vn-autocomplete[field="$ctrl.item.originFk"]`,
expenceAutocomplete: `vn-autocomplete[field="$ctrl.item.expenceFk"]`,
longNameInput: `vn-textfield[field="$ctrl.item.longName"] input`,
isActiveCheckbox: `vn-check[label='Active'] md-checkbox`,
typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
nameInput: 'vn-textfield[label="Name"] input',
relevancyInput: 'vn-input-number[label="Relevancy"] input',
originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
expenceAutocomplete: 'vn-autocomplete[field="$ctrl.item.expenceFk"]',
longNameInput: 'vn-textfield[field="$ctrl.item.longName"] input',
isActiveCheckbox: 'vn-check[label="Active"] md-checkbox',
submitBasicDataButton: `${components.vnSubmit}`
},
itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
tagsButton: `vn-left-menu a[ui-sref="item.card.tags"]`,
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`,
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"]`,
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`,
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"]`,
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`,
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`,
seventhRelevancyInput: `vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input`,
addItemTagButton: `vn-item-tags vn-icon-button[icon="add_circle"]`,
tagsButton: 'vn-left-menu a[ui-sref="item.card.tags"]',
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',
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"]',
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',
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"]',
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',
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',
seventhRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input',
addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
},
itemTax: {
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"]`,
thirdClassAutocomplete: `vn-item-tax vn-horizontal:nth-child(3) > 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"]',
thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[field="tax.taxClassFk"]',
submitTaxButton: `vn-item-tax ${components.vnSubmit}`
},
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}`,
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: {
addNicheButton: `vn-item-niche vn-icon[icon="add_circle"]`,
firstWarehouseAutocomplete: `vn-item-niche vn-autocomplete[field="niche.warehouseFk"]`,
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"]`,
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"]`,
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`,
addNicheButton: 'vn-item-niche vn-icon[icon="add_circle"]',
firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[field="niche.warehouseFk"]',
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"]',
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"]',
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',
submitNichesButton: `vn-item-niche ${components.vnSubmit}`
},
itemBotanical: {
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
genusAutocomplete: `vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]`,
speciesAutocomplete: `vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]`,
genusAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]',
speciesAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]',
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
},
itemSummary: {
basicData: `vn-item-summary [name="basicData"]`,
vat: `vn-item-summary [name="tax"]`,
tags: `vn-item-summary [name="tags"]`,
niche: `vn-item-summary [name="niche"]`,
botanical: `vn-item-summary [name="botanical"]`,
barcode: `vn-item-summary [name="barcode"]`
basicData: 'vn-item-summary [name="basicData"]',
vat: 'vn-item-summary [name="tax"]',
tags: 'vn-item-summary [name="tags"]',
niche: 'vn-item-summary [name="niche"]',
botanical: 'vn-item-summary [name="botanical"]',
barcode: 'vn-item-summary [name="barcode"]'
},
itemDiary: {
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',
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)',
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: {
newTicketButton: 'vn-ticket-index > a',
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)`,
searchResultAddress: `vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)`,
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(5)',
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
searchButton: `vn-ticket-index vn-searchbar vn-icon[icon="search"]`,
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`,
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`,
sixthWeeklyTicketDeleteIcon: `vn-ticket-weekly > form vn-tbody > vn-tr:nth-child(6) > vn-td:nth-child(6) > vn-icon-button[icon="delete"]`
searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]',
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: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',
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"]'
},
createTicketView: {
clientAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.clientFk"]',
@ -325,57 +325,59 @@ export default {
createButton: `${components.vnSubmit}`
},
ticketDescriptor: {
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)`,
moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(5)',
moreMenuAddToTurn: `vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(2)`,
moreMenuDeleteTicket: `vn-ticket-descriptor vn-icon-menu > div > vn-drop-down > vn-popover ul > li:nth-child(3)`,
moreMenu: 'vn-ticket-descriptor vn-icon-menu > div > vn-icon',
moreMenuAddStowaway: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Add stowaway"]',
moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Remove stowaway"]',
moreMenuAddToTurn: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Add turn"]',
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)',
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"]',
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)`,
saturdayButton: `vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)`,
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)',
closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]',
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"]'
},
ticketNotes: {
firstNoteRemoveButton: `vn-icon[icon="delete"]`,
addNoteButton: `vn-icon[icon="add_circle"]`,
firstNoteTypeAutocomplete: `vn-autocomplete[field="observation.observationTypeFk"]`,
firstDescriptionInput: `vn-textfield[label="Description"] input`,
firstNoteRemoveButton: 'vn-icon[icon="delete"]',
addNoteButton: 'vn-icon[icon="add_circle"]',
firstNoteTypeAutocomplete: 'vn-autocomplete[field="observation.observationTypeFk"]',
firstDescriptionInput: 'vn-textfield[label="Description"] input',
submitNotesButton: `${components.vnSubmit}`
},
ticketExpedition: {
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"]`,
acceptDeleteRowButton: `vn-ticket-expedition > vn-confirm[vn-id="delete-expedition"] button[response=ACCEPT]`,
expeditionRow: `vn-ticket-expedition vn-table vn-tbody > vn-tr`
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"]',
acceptDeleteRowButton: 'vn-ticket-expedition > vn-confirm[vn-id="delete-expedition"] button[response=ACCEPT]',
expeditionRow: 'vn-ticket-expedition vn-table vn-tbody > vn-tr'
},
ticketPackages: {
packagesButton: `vn-left-menu a[ui-sref="ticket.card.package.index"]`,
firstPackageAutocomplete: `vn-autocomplete[label="Package"]`,
firstQuantityInput: `vn-input-number[label="Quantity"] input`,
firstRemovePackageButton: `vn-icon-button[vn-tooltip="Remove package"]`,
addPackageButton: `vn-icon-button[vn-tooltip="Add package"]`,
clearPackageAutocompleteButton: `vn-autocomplete[label="Package"] > div > div > div > vn-icon > i`,
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
firstQuantityInput: 'vn-input-number[label="Quantity"] input',
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] > div > div > div > vn-icon > i',
savePackagesButton: `${components.vnSubmit}`
},
ticketSales: {
saleButton: `vn-left-menu a[ui-sref="ticket.card.sale"]`,
saleLine: `vn-table div > vn-tbody > vn-tr`,
saleButton: 'vn-left-menu a[ui-sref="ticket.card.sale"]',
saleLine: 'vn-table div > vn-tbody > vn-tr',
saleDescriptorPopover: 'vn-ticket-sale vn-item-descriptor-popover > vn-popover',
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"]',
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',
firstSaleZoomedImage: 'body > div > div > img',
firstSaleQuantity: `vn-textfield[model="sale.quantity"]:nth-child(1) input`,
firstSaleQuantityCell: `vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable`,
firstSaleQuantityClearInput: `vn-textfield[model="sale.quantity"] div.suffix > i`,
firstSaleQuantity: 'vn-textfield[model="sale.quantity"]:nth-child(1) input',
firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable',
firstSaleQuantityClearInput: 'vn-textfield[model="sale.quantity"] div.suffix > i',
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',
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',
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)',
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)`,
firstSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"] md-checkbox`,
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)',
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',
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`,
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)`,
secondSaleText: `vn-table div > vn-tbody > vn-tr:nth-child(2)`,
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',
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)',
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',
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`,
thirdSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(3) vn-check[field="sale.checked"] 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',
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"]',
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',
moveToTicketInputClearButton: 'vn-popover.shown i[title="Clear"]',
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"]',
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]`,
stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"] button',
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)'
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]',
stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"]'
},
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}`,
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.stateFk"]',
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: {
basicDataButton: `vn-left-menu a[ui-sref="ticket.card.data.stepOne"]`,
clientAutocomplete: `vn-autocomplete[field="$ctrl.clientFk"]`,
addressAutocomplete: `vn-autocomplete[field="$ctrl.ticket.addressFk"]`,
agencyAutocomplete: `vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]`,
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`,
stepTwoTotalPriceDif: `vn-ticket-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)`,
chargesReasonAutocomplete: `vn-autocomplete[field="$ctrl.ticket.option"]`,
basicDataButton: 'vn-left-menu a[ui-sref="ticket.card.basicData.stepOne"]',
clientAutocomplete: 'vn-autocomplete[field="$ctrl.clientFk"]',
addressAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.addressFk"]',
agencyAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]',
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',
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"]',
},
ticketComponents: {
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'
},
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',
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"]`,
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`,
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`,
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"]',
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',
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',
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: {
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',
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'
@ -464,25 +459,25 @@ export default {
saveServiceButton: `${components.vnSubmit}`
},
createStateView: {
stateAutocomplete: `vn-autocomplete[field="$ctrl.stateFk"]`,
workerAutocomplete: `vn-autocomplete[field="$ctrl.workerFk"]`,
clearStateInputButton: `vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i`,
stateAutocomplete: 'vn-autocomplete[field="$ctrl.stateFk"]',
workerAutocomplete: 'vn-autocomplete[field="$ctrl.workerFk"]',
clearStateInputButton: 'vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i',
saveStateButton: `${components.vnSubmit}`
},
claimsIndex: {
searchClaimInput: `vn-claim-index ${components.vnTextfield}`,
searchResult: `vn-claim-index vn-card > div > vn-table > div > vn-tbody > a`,
searchButton: `vn-claim-index vn-searchbar vn-icon[icon="search"]`
searchResult: 'vn-claim-index vn-card > div > vn-table > div > vn-tbody > a',
searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]'
},
claimBasicData: {
claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[field="$ctrl.claim.claimStateFk"]',
isPaidWithManaCheckbox: 'vn-check[field="$ctrl.claim.isChargedToMana"] md-checkbox',
responsabilityInputRange: `vn-input-range`,
observationInput: `vn-textarea[label="Observation"] textarea`,
responsabilityInputRange: 'vn-input-range',
observationInput: 'vn-textarea[label="Observation"] textarea',
saveButton: `${components.vnSubmit}`
},
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',
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',
@ -516,29 +511,29 @@ export default {
},
ordersIndex: {
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)`,
searchResultAddress: `vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)`,
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)',
searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)',
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}`,
},
createOrderView: {
clientAutocomplete: `vn-autocomplete[label="Client"]`,
addressAutocomplete: `vn-autocomplete[label="Address"]`,
agencyAutocomplete: `vn-autocomplete[label="Agency"]`,
landedDatePicker: `vn-date-picker[label="Landed"]`,
clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
landedDatePicker: 'vn-date-picker[label="Landed"]',
createButton: `${components.vnSubmit}`,
cancelButton: `vn-button[href="#!/client/index"]`
cancelButton: 'vn-button[href="#!/client/index"]'
},
orderCatalog: {
orderByAutocomplete: `vn-autocomplete[label="Order by"]`,
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
},
orderBasicData: {
clientAutocomplete: `vn-autocomplete[label="Client"]`,
addressAutocomplete: `vn-autocomplete[label="Address"]`,
agencyAutocomplete: `vn-autocomplete[label="Agency"]`,
observationInput: `vn-textarea[label="Observation"] textarea`,
clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
observationInput: 'vn-textarea[label="Observation"] textarea',
saveButton: `${components.vnSubmit}`
},
orderLine: {

View File

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

View File

@ -114,7 +114,7 @@ describe('Client Edit basicData path', () => {
.write(selectors.clientBasicData.mobileInput, '987654321')
.clearInput(selectors.clientBasicData.emailInput)
.write(selectors.clientBasicData.emailInput, 'Storm@verdnatura.es')
.autocompleteSearch(selectors.clientBasicData.salesPersonAutocomplete, 'AccessoryNick')
.autocompleteSearch(selectors.clientBasicData.salesPersonAutocomplete, 'replenisherNick')
.autocompleteSearch(selectors.clientBasicData.channelAutocomplete, 'Metropolis newspaper')
.waitToClick(selectors.clientBasicData.saveButton)
.waitForLastSnackbar();
@ -162,7 +162,7 @@ describe('Client Edit basicData path', () => {
const result = await nightmare
.waitToGetProperty(`${selectors.clientBasicData.salesPersonAutocomplete} input`, 'value');
expect(result).toEqual('accessoryNick');
expect(result).toEqual('replenisherNick');
});
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 createNightmare from '../../helpers/nightmare';
describe('Client risk path', () => {
describe('Client balance path', () => {
const nightmare = createNightmare();
beforeAll(() => {
@ -19,10 +19,10 @@ describe('Client risk path', () => {
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
.accessToSection('client.card.risk.index')
.waitToGetProperty(`${selectors.clientRisk.companyAutocomplete} input`, 'value');
.accessToSection('client.card.balance.index')
.waitToGetProperty(`${selectors.clientBalance.companyAutocomplete} input`, 'value');
expect(result).toEqual('CCs');
});
@ -38,19 +38,19 @@ describe('Client risk path', () => {
it('should click the new payment button', async() => {
let url = await nightmare
.reloadSection('client.card.risk.index')
.waitToClick(selectors.clientRisk.newPaymentButton)
.waitForURL('/risk')
.reloadSection('client.card.balance.index')
.waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/balance')
.parsedUrl();
expect(url.hash).toContain('/risk');
expect(url.hash).toContain('/balance');
});
it('should create a new payment that clears the debt', async() => {
let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentBankInut)
.write(selectors.clientRisk.newPaymentBankInut, '2')
.waitToClick(selectors.clientRisk.saveButton)
.clearInput(selectors.clientBalance.newPaymentBankInut)
.write(selectors.clientBalance.newPaymentBankInut, '2')
.waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar();
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() => {
let company = await nightmare
.waitToGetProperty(`${selectors.clientRisk.companyAutocomplete} input`, 'value');
.waitToGetProperty(`${selectors.clientBalance.companyAutocomplete} input`, 'value');
let firstRiskLineBalance = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText');
let firstBalanceLine = await nightmare
.waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(company).toEqual('VNL');
expect(firstRiskLineBalance).toContain('0.00');
expect(firstBalanceLine).toContain('0.00');
});
it('should now click the new payment button', async() => {
let url = await nightmare
.waitToClick(selectors.clientRisk.newPaymentButton)
.waitForURL('/risk')
.waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/balance')
.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() => {
let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentAmountInput)
.write(selectors.clientRisk.newPaymentAmountInput, '100')
.waitToClick(selectors.clientRisk.saveButton)
.clearInput(selectors.clientBalance.newPaymentAmountInput)
.write(selectors.clientBalance.newPaymentAmountInput, '100')
.waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar();
expect(result).toContain('Data saved!');
@ -89,26 +89,26 @@ describe('Client risk path', () => {
it('should check balance is now 100', async() => {
let result = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText');
.waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(result).toContain('100.00');
});
it('should again click the new payment button', async() => {
let url = await nightmare
.waitToClick(selectors.clientRisk.newPaymentButton)
.waitForURL('/risk')
.waitToClick(selectors.clientBalance.newPaymentButton)
.waitForURL('/balance')
.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() => {
let result = await nightmare
.clearInput(selectors.clientRisk.newPaymentAmountInput)
.write(selectors.clientRisk.newPaymentAmountInput, '-150')
.clearInput(selectors.clientBalance.newPaymentAmountInput)
.write(selectors.clientBalance.newPaymentAmountInput, '-150')
.wait(1999)
.waitToClick(selectors.clientRisk.saveButton)
.waitToClick(selectors.clientBalance.saveButton)
.waitForLastSnackbar();
expect(result).toContain('Data saved!');
@ -116,7 +116,7 @@ describe('Client risk path', () => {
it('should check balance is now -50', async() => {
let result = await nightmare
.waitToGetProperty(selectors.clientRisk.firstRiskLineBalance, 'innerText');
.waitToGetProperty(selectors.clientBalance.firstBalanceLine, 'innerText');
expect(result).toContain('€50.00');
});
@ -143,20 +143,20 @@ describe('Client risk path', () => {
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
.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker')
.waitToClick(selectors.clientsIndex.searchResult)
.waitToClick(selectors.clientRisk.riskButton)
.waitForURL('/risk')
.waitToClick(selectors.clientBalance.balanceButton)
.waitForURL('/balance')
.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() => {
let result = await nightmare
.exists(selectors.clientRisk.newPaymentButton);
.exists(selectors.clientBalance.newPaymentButton);
expect(result).toBeFalsy();
});

View File

@ -84,7 +84,7 @@ describe('Item summary path', () => {
const result = await nightmare
.clearInput('vn-item-index vn-searchbar input')
.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)
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
.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() => {
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);
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() => {
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');
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() => {
@ -172,10 +172,10 @@ describe('Item summary path', () => {
it(`should check the item summary shows fields from basic data section`, async() => {
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');
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() => {

View File

@ -7,8 +7,8 @@ describe('Item Edit basic data path', () => {
beforeAll(() => {
nightmare
.loginAndModule('buyer', 'item')
.accessToSearchResult('Melee weapon combat first 15cm')
.accessToSection('item.card.data');
.accessToSearchResult('Melee weapon combat fist 15cm')
.accessToSection('item.card.basicData');
});
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() => {
const result = await nightmare
.reloadSection('item.card.data')
.reloadSection('item.card.basicData')
.waitToGetProperty(selectors.itemBasicData.nameInput, 'value');
expect(result).toEqual('Rose of Purity');

View File

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

View File

@ -7,7 +7,7 @@ describe('Item descriptor path', () => {
nightmare
.loginAndModule('buyer', 'item')
.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() => {
@ -30,7 +30,7 @@ describe('Item descriptor path', () => {
it('should reload the section and check the inactive icon is bright', async() => {
let brightIcon = await nightmare
.reloadSection('item.card.data')
.reloadSection('item.card.basicData')
.waitForClassPresent(selectors.itemDescriptor.inactiveIcon, 'bright')
.isVisible(selectors.itemDescriptor.inactiveIcon);

View File

@ -8,7 +8,7 @@ describe('Ticket Create packages path', () => {
return nightmare
.loginAndModule('employee', 'ticket')
.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() => {
@ -53,7 +53,7 @@ describe('Ticket Create packages path', () => {
it(`should confirm the first select is the expected one`, async() => {
const result = await nightmare
.reloadSection('ticket.card.package.index')
.reloadSection('ticket.card.package')
.waitForTextInInput(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'Container medical box 1m')
.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() => {
let result = await nightmare
.autocompleteSearch(selectors.createStateView.workerAutocomplete, 'accessory')
.autocompleteSearch(selectors.createStateView.workerAutocomplete, 'replenisher')
.waitToClick(selectors.createStateView.saveStateButton)
.waitForLastSnackbar();

View File

@ -8,7 +8,7 @@ describe('Ticket Edit basic data path', () => {
return nightmare
.loginAndModule('employee', 'ticket')
.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() => {

View File

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

View File

@ -148,4 +148,43 @@ describe('Ticket descriptor path', () => {
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(() => {
nightmare
.loginAndModule('employee', 'ticket')
.accessToSearchResult('1')
.accessToSearchResult(8)
.accessToSection('ticket.card.service');
});

View File

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

View File

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

View File

@ -277,6 +277,7 @@ export default class DropDown extends Component {
if (data) {
for (let i = 0; i < data.length; i++) {
let option = data[i];
option.orgShowField = option[this.showField];
if (this.translateFields) {
option = Object.assign({}, option);
@ -285,6 +286,8 @@ export default class DropDown extends Component {
}
let li = this.document.createElement('li');
li.setAttribute('name', option.orgShowField);
fragment.appendChild(li);
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 {
constructor($element, $scope) {
super($element, $scope);
this._checkAll = false;
this._checked = false;
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;
this.switchChecks();
/**
* Sets the array model instance
* 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;
this.data.forEach(el => {
el[this.checkField] = this._checkAll;
/**
* Gets current check state
*/
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'),
controller: MultiCheck,
bindings: {
data: '=',
model: '<',
checkField: '<?',
checkAll: '=?',
disabled: '<?'

View File

@ -1,4 +1,5 @@
import './multi-check.js';
import crudModel from 'core/mocks/crud-model';
describe('Component vnMultiCheck', () => {
let controller;
@ -9,33 +10,94 @@ describe('Component vnMultiCheck', () => {
beforeEach(angular.mock.inject($componentController => {
$element = angular.element(`<div class="shown"></div>`);
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', () => {
it(`should set controller _checkAll property with the argument received then call switchChecks()`, () => {
let argument = 'I am the model';
spyOn(controller, 'switchChecks');
controller.checkAll = argument;
describe('checked() setter', () => {
it(`should set controller _checked property with the argument received then call toggle()`, () => {
spyOn(controller, 'toggle');
controller.checked = crudModel;
expect(controller._checkAll).toEqual(argument);
expect(controller.switchChecks).toHaveBeenCalledWith();
expect(controller._checked).toEqual(crudModel);
expect(controller.toggle).toHaveBeenCalledWith();
});
});
describe('switchChecks()', () => {
describe('toggle()', () => {
it(`should set checked property inside each existing element`, () => {
controller.data = [
{name: 'name'},
{name: null}
];
const data = controller.model.data;
expect(controller.data[0].checked).not.toBeDefined();
expect(controller.data[1].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(data[0].checked).not.toBeDefined();
expect(data[1].checked).not.toBeDefined();
expect(data[2].checked).not.toBeDefined();
expect(controller.data[0].checked).toBeTruthy();
expect(controller.data[1].checked).toBeTruthy();
controller._checked = true;
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,
'included': item.selected == 1,
'excluded': item.selected == 0
}">
}" vn-draggable="{{::$ctrl.draggable}}" vn-droppable="{{::$ctrl.droppable}}" on-drop="$ctrl.onDrop(item, dragged, dropped)">
<vn-horizontal>
<vn-auto class="actions">
<vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}"
ng-click="$ctrl.toggle(item, $event)">
</vn-icon>
</vn-auto>
<vn-one class="description">
<vn-check vn-auto vn-acl="{{$ctrl.aclRole}}"
ng-if="$ctrl.selectable"
field="item.selected"
@ -20,12 +19,11 @@
on-change="$ctrl.select(item, value)"
triple-state="true">
</vn-check>
{{::item.name}}
</vn-one>
<vn-one class="description">{{::item.name}}</vn-one>
<vn-auto>
<vn-icon-button icon="{{icon.icon}}"
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-icon-button>
</vn-auto>
@ -34,7 +32,10 @@
selectable="$ctrl.selectable"
disabled="$ctrl.disabled"
editable="$ctrl.editable"
icons="$ctrl.icons"
draggable="::$ctrl.draggable"
droppable="::$ctrl.droppable"
icons="::$ctrl.icons"
parent-scope="::$ctrl.parentScope"
acl-role="$ctrl.aclRole">
</vn-treeview-child>
</li>

View File

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

View File

@ -1,9 +1,12 @@
<vn-treeview-child
<vn-treeview-child acl-role="$ctrl.aclRole"
items="$ctrl.data"
parent="$ctrl.data"
selectable="$ctrl.selectable"
editable="$ctrl.editable"
disabled="$ctrl.disabled"
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>

View File

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

View File

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

View File

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

View File

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

View File

@ -24,5 +24,5 @@ window.ngModule = function(moduleName) {
let testsContext = require.context('./', true, /\.spec\.js$/);
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);

View File

@ -41,5 +41,7 @@
"City cannot be empty": "City cannot be empty",
"EXTENSION_INVALID_FORMAT": "Invalid extension",
"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",
"This client can't be invoiced": "Este cliente 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 doesn't exists": "That item doesn't exists"
"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",
"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';
xdescribe('Agency', () => {
describe('Component vnZoneCard', () => {
describe('Agency Component vnZoneCard', () => {
let $scope;
let controller;
let $httpBackend;
@ -33,5 +32,4 @@ xdescribe('Agency', () => {
});
});
});
});

View File

@ -1,8 +1,7 @@
import './index';
import watcher from 'core/mocks/watcher';
xdescribe('Agency', () => {
describe('Component vnZoneCreate', () => {
describe('Agency Component vnZoneCreate', () => {
let $scope;
let $state;
let controller;
@ -33,8 +32,8 @@ xdescribe('Agency', () => {
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';
xdescribe('Agency', () => {
describe('Component vnZoneIndex', () => {
describe('Agency Component vnZoneIndex', () => {
let $componentController;
let controller;
@ -29,7 +28,7 @@ xdescribe('Agency', () => {
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 value = 'My Delivery';
let result = controller.exprBuilder(param, value);
@ -38,4 +37,3 @@ xdescribe('Agency', () => {
});
});
});
});

View File

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

View File

@ -99,7 +99,7 @@ module.exports = Self => {
let stmt;
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
LEFT JOIN client c ON c.id = cl.clientFk
LEFT JOIN worker w ON w.id = cl.workerFk

View File

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

View File

@ -26,7 +26,7 @@
value="{{$ctrl.claim.claimState.description}}">
</vn-label-value>
<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 label="Salesperson"
value="{{$ctrl.claim.client.salesPerson.user.nickname}}">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,6 @@ describe('Client get', () => {
expect(result.id).toEqual(101);
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() => {
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() => {
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() => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import './index';
describe('Client', () => {
describe('Component vnClientRiskIndex', () => {
describe('Component vnClientBalanceIndex', () => {
let $componentController;
let $scope;
let $httpBackend;
@ -15,7 +15,7 @@ describe('Client', () => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new();
controller = $componentController('vnClientRiskIndex', {$scope});
controller = $componentController('vnClientBalanceIndex', {$scope});
}));
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-autocomplete
vn-one
label="Pay method"
label="Billing data"
vn-acl="salesAssistant"
field="$ctrl.client.payMethodFk"
url="/client/api/PayMethods"

View File

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

View File

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

View File

@ -12,7 +12,7 @@
{"state": "client.card.note.index", "icon": "insert_drive_file"},
{"state": "client.card.credit.index", "icon": "credit_card"},
{"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.log", "icon": "history"},
{
@ -82,7 +82,7 @@
"url": "/billing-data",
"state": "client.card.billingData",
"component": "vn-client-billing-data",
"description": "Pay method",
"description": "Billing data",
"params": {
"client": "$ctrl.client"
}
@ -188,22 +188,22 @@
"component": "vn-client-mandate",
"description": "Mandates"
}, {
"url": "/risk",
"url": "/balance",
"abstract": true,
"state": "client.card.risk",
"state": "client.card.balance",
"component": "ui-view"
}, {
"url": "/index",
"state": "client.card.risk.index",
"component": "vn-client-risk-index",
"state": "client.card.balance.index",
"component": "vn-client-balance-index",
"description": "Balance",
"params": {
"client": "$ctrl.client"
}
}, {
"url": "/create?payed&companyFk&bankFk&payedAmount",
"state": "client.card.risk.create",
"component": "vn-client-risk-create",
"state": "client.card.balance.create",
"component": "vn-client-balance-create",
"description": "New payment",
"params": {
"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;
stmt = new ParameterizedSQL(`
SELECT tc.description as type, SUM(ROUND(s.quantity * s.price * (100 - s.discount) / 100,2)) AS base
FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.itemTaxCountry itc ON itc.itemFk = s.itemFk
JOIN vn.country c ON c.id = itc.countryFK AND c.id = ?
LEFT JOIN vn.taxClass tc ON tc.id = itc.taxClassFk
WHERE t.refFk = ?
GROUP BY type`, [summary.invoiceOut.supplier().countryFk, summary.invoiceOut.ref]);
SELECT iot.* , pgc.*, IF(pe.equFk IS NULL, taxableBase, 0) AS Base, pgc.rate / 100 as vatPercent
FROM vn.invoiceOutTax iot
JOIN vn.pgc ON pgc.code = iot.pgcFk
LEFT JOIN vn.pgcEqu pe ON pe.equFk = pgc.code
WHERE invoiceOutFk = ?`, [summary.invoiceOut.id]);
let invoiceOutTaxesIndex = stmts.push(stmt) - 1;
stmts.push(

View File

@ -1,4 +1,8 @@
module.exports = Self => {
require('../methods/invoiceOut/filter')(Self);
require('../methods/invoiceOut/summary')(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"
},
"hasPdf": {
"type": "Number",
"type": "Boolean",
"mysql": {
"columnName": "pdf"
}

View File

@ -6,6 +6,16 @@
<a translate-attr="{title: 'Preview'}" ui-sref="invoiceOut.card.summary({id: $ctrl.invoiceOut.id})">
<vn-icon icon="desktop_windows"></vn-icon>
</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 class="body">
<div class="attributes">
@ -40,14 +50,16 @@
icon="{{::$ctrl.quicklinks.btnTwo.icon}}">
</vn-icon>
</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>
<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';
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) {
this._invoiceOut = value;
@ -24,6 +52,39 @@ class Controller {
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 = {}) {
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', {
template: require('./index.html'),

View File

@ -2,3 +2,10 @@ Volume exceded: Volumen excedido
Volume: Volumen
Client card: Ficha del cliente
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-id="model"
url="/api/InvoiceOuts"
filter="::$ctrl.filter"
url="/api/InvoiceOuts/filter"
limit="20"
data="invoicesOut"
order="issued DESC"
auto-load="false">
data="invoiceOuts"
order="issued DESC">
</vn-crud-model>
<div class="content-block">
<div class="vn-list">
<vn-card pad-medium-h>
<vn-searchbar
panel="vn-invoice-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
auto-load="true"
on-search="$ctrl.onSearch($params)"
info="Search invoices by reference"
vn-focus>
</vn-searchbar>
@ -36,7 +32,7 @@
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="invoiceOut in invoicesOut"
<a ng-repeat="invoiceOut in invoiceOuts"
class="clickable vn-tr searchResult"
ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})">
<vn-td>{{::invoiceOut.ref | dashIfEmpty}}</vn-td>
@ -46,11 +42,11 @@
<span
class="link"
ng-click="$ctrl.showClientDescriptor($event, invoiceOut.clientFk)">
{{::invoiceOut.client.name | dashIfEmpty}}
{{::invoiceOut.clientSocialName | dashIfEmpty}}
</span>
</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>
<vn-icon-button

View File

@ -5,42 +5,6 @@ export default class Controller {
this.accessToken = vnToken.token;
this.$ = $scope;
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) {
@ -68,6 +32,13 @@ export default class Controller {
event.preventDefault();
event.stopImmediatePropagation();
}
onSearch(params) {
if (params)
this.$.model.applyFilter(null, params);
else
this.$.model.clear();
}
}
Controller.$inject = ['$scope', 'vnToken'];

View File

@ -14,25 +14,31 @@
<vn-label-value label="Booked"
value="{{$ctrl.summary.invoiceOut.booked | dateTime: 'dd/MM/yyyy'}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Company"
value="{{$ctrl.summary.invoiceOut.company.code | dashIfEmpty}}">
</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-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>
<h4 translate>Ticket</h4>
<vn-table model="model">
@ -40,6 +46,7 @@
<vn-tr>
<vn-th number>Ticket id</vn-th>
<vn-th>Alias</vn-th>
<vn-th>Shipped</vn-th>
<vn-th number>Amount</vn-th>
</vn-tr>
</vn-thead>
@ -59,6 +66,7 @@
{{ticket.nickname}}
</span>
</vn-td>
<vn-td>{{ticket.shipped | dateTime: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{ticket.total | currency: 'EUR': 2}}</vn-td>
</vn-tr>
</vn-tbody>

View File

@ -18,13 +18,6 @@ class Controller {
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() {
this.$http.get(`/api/InvoiceOuts/${this.invoiceOut.id}/summary`).then(response => {
this.summary = response.data;
@ -36,7 +29,6 @@ class Controller {
this.$.clientDescriptor.parent = event.target;
this.$.clientDescriptor.show();
event.preventDefault();
event.stopImmediatePropagation();
}
showTicketDescriptor(event, ticketFk) {
@ -44,14 +36,12 @@ class Controller {
this.$.ticketDescriptor.parent = event.target;
this.$.ticketDescriptor.show();
event.preventDefault();
event.stopImmediatePropagation();
}
preview(event, invoiceOut) {
this.selectedInvoiceOut = invoiceOut;
this.$.invoiceOutSummaryDialog.show();
event.preventDefault();
event.stopImmediatePropagation();
}
}

View File

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

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