Merge branch 'dev' into test
gitea/salix/test This commit looks good
Details
gitea/salix/test This commit looks good
Details
This commit is contained in:
commit
e7ca55fea3
|
@ -26,7 +26,7 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.send = async(ctx, data, transaction) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const userId = ctx.options && ctx.options.accessToken.userId || ctx.req && ctx.req.accessToken.userId;
|
||||
const models = Self.app.models;
|
||||
const sender = await models.Account.findById(userId, transaction);
|
||||
const recipient = await models.Account.findById(data.recipientFk, transaction);
|
||||
|
|
|
@ -55,13 +55,15 @@
|
|||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"property": "logout",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"property": "validateToken",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
[client]
|
||||
enable_cleartext_plugin = ON
|
||||
host = localhost
|
||||
port = 3306
|
||||
user = root
|
||||
password = password
|
||||
ssl-mode = DISABLED
|
|
@ -1,5 +1,4 @@
|
|||
#!/bin/bash
|
||||
#IMPORTANT Any changes in this file are to applyed to mirror file export-data.cmd
|
||||
echo "USE \`account\`;" > install/dump/dumpedFixtures.sql
|
||||
mysqldump --defaults-file=connect.ini --no-create-info account role roleRole roleInherit >> install/dump/dumpedFixtures.sql
|
||||
echo "USE \`salix\`;" >> install/dump/dumpedFixtures.sql
|
|
@ -10,7 +10,7 @@ echo "[INFO] -> Imported ./dump/mysqlPlugins.sql"
|
|||
mysql -u root -f < ./dump/mysqlPlugins.sql
|
||||
|
||||
# Import changes
|
||||
for file in changes/*/*.sql; do
|
||||
for file in changes/*.sql; do
|
||||
echo "[INFO] -> Imported ./$file"
|
||||
mysql -u root -fc < $file
|
||||
done
|
|
@ -0,0 +1,29 @@
|
|||
USE `hedera`;
|
||||
DROP procedure IF EXISTS `orderGetTotal`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `hedera`$$
|
||||
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `orderGetTotal`()
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula el total con IVA para un conjunto de orders.
|
||||
*
|
||||
* @table tmp.order(orderFk) Identificadores de las ordenes a calcular
|
||||
* @return tmp.orderTotal Total para cada order
|
||||
*/
|
||||
CALL orderGetTax;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.orderTotal;
|
||||
CREATE TEMPORARY TABLE tmp.orderTotal
|
||||
(INDEX (orderFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT o.orderFk, IFNULL(SUM(ot.taxableBase + ot.tax), 0.0) AS total
|
||||
FROM tmp.order o
|
||||
LEFT JOIN tmp.orderAmount ot ON o.orderFk = ot.orderFk
|
||||
GROUP BY orderFk;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.orderTax;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
|
@ -0,0 +1,36 @@
|
|||
DROP PROCEDURE IF EXISTS vn.ticketGetVisibleAvailable;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`ticketGetVisibleAvailable`(
|
||||
vTicket INT)
|
||||
BEGIN
|
||||
DECLARE vVisibleCalc INT;
|
||||
DECLARE vAvailableCalc INT;
|
||||
DECLARE vShipped DATE;
|
||||
DECLARE vWarehouse TINYINT;
|
||||
DECLARE vAlertLevel INT;
|
||||
|
||||
SELECT t.warehouseFk, t.shipped, ts.alertLevel INTO vWarehouse, vShipped, vAlertLevel
|
||||
FROM ticket t
|
||||
LEFT JOIN ticketState ts ON ts.ticketFk = vTicket
|
||||
WHERE t.id = vTicket;
|
||||
|
||||
IF vAlertLevel IS NULL OR vAlertLevel = 0 THEN
|
||||
IF vShipped >= CURDATE() THEN
|
||||
CALL cache.available_refresh(vAvailableCalc, FALSE, vWarehouse, vShipped);
|
||||
END IF;
|
||||
IF vShipped = CURDATE() THEN
|
||||
CALL cache.visible_refresh(vVisibleCalc, FALSE, vWarehouse);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
SELECT s.id, s.itemFk, s.quantity, s.concept, s.price, s.reserved, s.discount, v.visible, av.available, it.image
|
||||
FROM sale s
|
||||
LEFT JOIN cache.visible v ON v.item_id = s.itemFk AND v.calc_id = vVisibleCalc
|
||||
LEFT JOIN cache.available av ON av.item_id = s.itemFk AND av.calc_id = vAvailableCalc
|
||||
LEFT JOIN item it ON it.id = s.itemFk
|
||||
WHERE s.ticketFk = vTicket
|
||||
ORDER BY s.concept;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,36 @@
|
|||
USE `hedera`;
|
||||
DROP FUNCTION IF EXISTS `orderGetTotal`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `hedera`$$
|
||||
|
||||
CREATE DEFINER=`root`@`%` FUNCTION `orderGetTotal`(vOrder INT) RETURNS decimal(10,2)
|
||||
READS SQL DATA
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
/**
|
||||
* Obtiene el total de un pedido con el IVA y el recargo de
|
||||
* equivalencia incluidos.
|
||||
*
|
||||
* @param vOrder El identificador del pedido
|
||||
* @return El total del pedido
|
||||
*/
|
||||
DECLARE vTotal DECIMAL(10,2);
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.order;
|
||||
CREATE TEMPORARY TABLE tmp.order
|
||||
ENGINE = MEMORY
|
||||
SELECT vOrder orderFk;
|
||||
|
||||
CALL orderGetTotal;
|
||||
|
||||
SELECT total INTO vTotal FROM tmp.orderTotal;
|
||||
|
||||
DROP TEMPORARY TABLE
|
||||
tmp.order,
|
||||
tmp.orderTotal;
|
||||
|
||||
RETURN vTotal;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
|
@ -0,0 +1,65 @@
|
|||
USE `hedera`;
|
||||
DROP procedure IF EXISTS `orderGetTax`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `hedera`$$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `orderGetTax`()
|
||||
READS SQL DATA
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula el IVA, y el recargo de equivalencia de un pedido
|
||||
* desglosados por tipos.
|
||||
*
|
||||
* @param vOrder El identificador del pedido
|
||||
* @treturn tmp.orderTax Bases imponibles, IVA y recargo de equivalencia
|
||||
*/
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.addressCompany;
|
||||
CREATE TEMPORARY TABLE tmp.addressCompany
|
||||
(INDEX (addressFk, companyFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT DISTINCT o.address_id addressFk, o.company_id companyFk
|
||||
FROM tmp.order tmpOrder
|
||||
JOIN hedera.order o ON o.id = tmpOrder.orderFk;
|
||||
|
||||
CALL vn.addressTaxArea ();
|
||||
|
||||
-- Calcula el IVA y el recargo desglosado.
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.orderTax;
|
||||
CREATE TEMPORARY TABLE tmp.orderTax
|
||||
(INDEX (orderFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT o.id orderFk,
|
||||
tc.code,
|
||||
SUM(m.amount * m.price) taxableBase,
|
||||
pgc.rate
|
||||
FROM tmp.order tmpOrder
|
||||
JOIN `order` o ON o.id = tmpOrder.orderFk
|
||||
JOIN orderRow m ON m.orderFk = o.id
|
||||
JOIN vn.item i ON i.id = m.itemFk
|
||||
JOIN vn.client c ON c.id = o.customer_id
|
||||
JOIN vn.supplier s ON s.id = o.company_id
|
||||
JOIN tmp.addressTaxArea ata
|
||||
ON ata.addressFk = o.address_id AND ata.companyFk = o.company_id
|
||||
JOIN vn.itemTaxCountry itc
|
||||
ON itc.itemFk = i.id AND itc.countryFk = s.countryFk
|
||||
JOIN vn.bookingPlanner bp
|
||||
ON bp.countryFk = s.countryFk
|
||||
AND bp.taxAreaFk = ata.areaFk
|
||||
AND bp.taxClassFk = itc.taxClassFk
|
||||
JOIN vn.pgc ON pgc.code = bp.pgcFk
|
||||
JOIN vn.taxClass tc ON tc.id = bp.taxClassFk
|
||||
GROUP BY tmpOrder.orderFk, pgc.code,pgc.rate
|
||||
HAVING taxableBase != 0;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.orderAmount;
|
||||
CREATE TEMPORARY TABLE tmp.orderAmount
|
||||
(INDEX (orderFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT orderFk, taxableBase, SUM(CAST(taxableBase * rate / 100 AS DECIMAL(10, 2))) tax,code
|
||||
FROM tmp.orderTax
|
||||
GROUP BY orderFk, code;
|
||||
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* Añadir a producción cuando se suba salix */
|
||||
|
||||
DROP TRIGGER IF EXISTS vn2008.ConsignatariosAfterUpdate;
|
||||
USE vn2008;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`%` TRIGGER `vn2008`.`ConsignatariosAfterUpdate` AFTER UPDATE ON `Consignatarios` FOR EACH ROW
|
||||
BEGIN
|
||||
|
||||
-- Recargos de equivalencia distintos implican facturacion por consignatario
|
||||
IF NEW.isEqualizated != OLD.isEqualizated THEN
|
||||
|
||||
IF
|
||||
(SELECT COUNT(*) FROM
|
||||
(
|
||||
SELECT DISTINCT (isEqualizated = FALSE) as Equ
|
||||
FROM Consignatarios
|
||||
WHERE Id_Cliente = NEW.Id_Cliente
|
||||
) t1
|
||||
) > 1
|
||||
|
||||
THEN
|
||||
|
||||
UPDATE Clientes
|
||||
SET invoiceByAddress = TRUE
|
||||
WHERE Id_Cliente = NEW.Id_Cliente;
|
||||
|
||||
END IF;
|
||||
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
USE vn;
|
|
@ -0,0 +1,27 @@
|
|||
/* Añadir a producción cuando se suba salix */
|
||||
|
||||
DROP TRIGGER IF EXISTS vn2008.ConsignatariosBeforeInsert;
|
||||
USE vn2008;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`%` TRIGGER `vn2008`.`ConsignatariosBeforeInsert`
|
||||
BEFORE INSERT ON `vn2008`.`Consignatarios`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
DECLARE vIsEqualizated BOOLEAN;
|
||||
|
||||
CALL pbx.phoneIsValid (NEW.telefono);
|
||||
CALL pbx.phoneIsValid (NEW.movil);
|
||||
|
||||
IF NEW.isEqualizated IS NULL THEN
|
||||
SELECT RE
|
||||
INTO vIsEqualizated
|
||||
FROM Clientes
|
||||
WHERE Id_Cliente = NEW.Id_Cliente;
|
||||
|
||||
SET NEW.isEqualizated = vIsEqualizated;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
USE vn;
|
|
@ -0,0 +1,16 @@
|
|||
/* Añadir a producción cuando se suba salix */
|
||||
|
||||
DROP TRIGGER IF EXISTS vn2008.ConsignatariosBeforeUpdate;
|
||||
USE vn2008;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`%` TRIGGER `vn2008`.`ConsignatariosBeforeUpdate`
|
||||
BEFORE UPDATE ON `vn2008`.`Consignatarios`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
CALL pbx.phoneIsValid (NEW.telefono);
|
||||
CALL pbx.phoneIsValid (NEW.movil);
|
||||
END$$
|
||||
DELIMITER ;
|
||||
USE vn;
|
|
@ -0,0 +1,3 @@
|
|||
/* Añadir a producción cuando se suba salix */
|
||||
|
||||
DROP TRIGGER vn2008.ClientesAfterInsert;
|
|
@ -0,0 +1,19 @@
|
|||
/* Añadir a producción cuando se suba salix */
|
||||
DROP TRIGGER IF EXISTS vn2008.ClientesAfterUpdate;
|
||||
USE vn2008;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`%` TRIGGER `vn2008`.`ClientesAfterUpdate`
|
||||
AFTER UPDATE ON `Clientes`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.default_address AND (NEW.default_address != OLD.default_address) THEN
|
||||
UPDATE Consignatarios SET predeterminada = FALSE
|
||||
WHERE Id_cliente = NEW.Id_cliente;
|
||||
|
||||
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
USE vn;
|
|
@ -0,0 +1,9 @@
|
|||
/* Script de migración consignatarios.
|
||||
Añadir a producción cuando se suba salix */
|
||||
|
||||
/* USE vn;
|
||||
|
||||
UPDATE vn.client c
|
||||
JOIN vn.address a ON a.clientFk = c.id AND a.isDefaultAddress
|
||||
SET c.defaultAddressFk = a.id
|
||||
WHERE c.defaultAddressFk IS NULL */
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,5 @@
|
|||
|
||||
|
||||
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
|
||||
ALTER TABLE `vn2008`.`Consignatarios` AUTO_INCREMENT = 1;
|
||||
|
||||
|
@ -8,7 +10,7 @@ INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`
|
|||
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@verdnatura.es')
|
||||
FROM `account`.`role`;
|
||||
|
||||
INSERT INTO `vn`.`worker`(`id`,`workerCode`, `firstName`, `name`, `userFk`)
|
||||
INSERT INTO `vn2008`.`Trabajadores`(`Id_Trabajador`,`CodigoTrabajador`, `Nombre`, `Apellidos`, `user_id`)
|
||||
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id
|
||||
FROM `vn`.`user`;
|
||||
|
||||
|
@ -29,7 +31,7 @@ INSERT INTO `account`.`user`(`id`,`name`,`password`,`role`,`active`,`email`,`lan
|
|||
(111, 'Missing', 'ac754a330530832ba1bf7687f577da91', 2 , 0, NULL, 'es'),
|
||||
(112, 'Trash', 'ac754a330530832ba1bf7687f577da91', 2 , 0, NULL, 'es');
|
||||
|
||||
INSERT INTO `vn`.`worker`(`workerCode`, `id`, `firstName`, `name`, `userFk`)
|
||||
INSERT INTO `vn2008`.`Trabajadores`(`CodigoTrabajador`, `Id_Trabajador`, `Nombre`, `Apellidos`, `user_id`)
|
||||
VALUES
|
||||
('LGN', 106, 'David Charles', 'Haller', 106),
|
||||
('ANT', 107, 'Hank' , 'Pym' , 107),
|
||||
|
@ -209,38 +211,65 @@ INSERT INTO `vn`.`clientManaCache`(`clientFk`, `mana`, `dated`)
|
|||
( 103, 0, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)),
|
||||
( 104, -30, DATE_ADD(CURDATE(), INTERVAL -1 MONTH));
|
||||
|
||||
INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `isDefaultAddress`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`)
|
||||
INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`)
|
||||
VALUES
|
||||
(101, 'address 01', 'Somewhere in Thailand', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(102, 'address 02', 'Somewhere in Poland', 'Silla', 46460, 1, 3333333333, 444444444, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(103, 'address 03', 'Somewhere in Japan', 'Silla', 46460, 1, 3333333333, 444444444, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(104, 'address 04', 'Somewhere in Spain', 'Silla', 46460, 1, 3333333333, 444444444, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(105, 'address 05', 'Somewhere in Potugal', 'Silla', 46460, 1, 5555555555, 666666666, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(106, 'address 06', 'Somewhere in UK', 'Silla', 46460, 1, 5555555555, 666666666, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(107, 'address 07', 'Somewhere in Valencia', 'Silla', 46460, 1, 5555555555, 666666666, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(108, 'address 08', 'Somewhere in Silla', 'Silla', 46460, 1, 5555555555, 666666666, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(109, 'address 09', 'Somewhere in London', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(110, 'address 10', 'Somewhere in Algemesi', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(111, 'address 11', 'Somewhere in Carlet', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(112, 'address 12', 'Somewhere in Campanar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(113, 'address 13', 'Somewhere in Malilla', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(114, 'address 14', 'Somewhere in France', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(115, 'address 15', 'Somewhere in Birmingham', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(116, 'address 16', 'Somewhere in Scotland', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(117, 'address 17', 'Somewhere in nowhere', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(118, 'address 18', 'Somewhere over the rainbow', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(119, 'address 19', 'Somewhere in Alberic', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(120, 'address 20', 'Somewhere in Montortal', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 109, 2, NULL, NULL, 0),
|
||||
(121, 'address 21', 'the bat cave', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 101, 2, NULL, NULL, 0),
|
||||
(122, 'address 22', 'NY roofs', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 102, 2, NULL, NULL, 0),
|
||||
(123, 'address 23', 'The phone box', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 103, 2, NULL, NULL, 0),
|
||||
(124, 'address 24', 'Stark tower', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 104, 2, NULL, NULL, 0),
|
||||
(125, 'address 25', 'The plastic cell', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 105, 2, NULL, NULL, 0),
|
||||
(126, 'address 26', 'Many places', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 106, 2, NULL, NULL, 0),
|
||||
(127, 'address 27', 'Your pocket', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 107, 2, NULL, NULL, 0),
|
||||
(128, 'address 28', 'Cerebro', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 108, 2, NULL, NULL, 0),
|
||||
(129, 'address 29', 'Luke Cages Bar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 0, 110, 2, NULL, NULL, 0),
|
||||
(130, 'address 30', 'Non valid address', 'Silla', 46460, 1, 1111111111, 222222222, 0, 0, 101, 2, NULL, NULL, 0);
|
||||
(1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 1),
|
||||
(2, 'Petter Parker', '20 Ingram Street', 'Silla', 46460, 1, 1111111111, 222222222, 1, 102, 2, NULL, NULL, 0, 1),
|
||||
(3, 'Clark Kent', '344 Clinton Street', 'Silla', 46460, 1, 1111111111, 222222222, 1, 103, 2, NULL, NULL, 0, 1),
|
||||
(4, 'Tony Stark', '10880 Malibu Point', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 1),
|
||||
(5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Silla', 46460, 1, 1111111111, 222222222, 1, 105, 2, NULL, NULL, 0, 1),
|
||||
(6, 'DavidCharlesHaller', 'Evil hideout', 'Silla', 46460, 1, 1111111111, 222222222, 1, 106, 2, NULL, NULL, 0, 1),
|
||||
(7, 'Hank Pym', 'Anthill', 'Silla', 46460, 1, 1111111111, 222222222, 1, 107, 2, NULL, NULL, 0, 1),
|
||||
(8, 'Charles Xavier', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Silla', 46460, 1, 1111111111, 222222222, 1, 108, 2, NULL, NULL, 0, 1),
|
||||
(9, 'Bruce Banner', 'Somewhere in New York', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 1),
|
||||
(10, 'Jessica Jones', 'NYCC 2015 Poster', 'Silla', 46460, 1, 1111111111, 222222222, 1, 110, 2, NULL, NULL, 0, 1),
|
||||
(11, 'Missing', 'The space', 'Silla', 46460, 1, 1111111111, 222222222, 1, 200, 2, NULL, NULL, 0, 1),
|
||||
(12, 'Trash', 'New York city', 'Silla', 46460, 1, 1111111111, 222222222, 1, 400, 2, NULL, NULL, 0, 1),
|
||||
(101, 'address 01', 'Somewhere in Thailand', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(102, 'address 02', 'Somewhere in Poland', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(103, 'address 03', 'Somewhere in Japan', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(104, 'address 04', 'Somewhere in Spain', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(105, 'address 05', 'Somewhere in Potugal', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(106, 'address 06', 'Somewhere in UK', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(107, 'address 07', 'Somewhere in Valencia', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(108, 'address 08', 'Somewhere in Silla', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(109, 'address 09', 'Somewhere in London', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(110, 'address 10', 'Somewhere in Algemesi', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(111, 'address 11', 'Somewhere in Carlet', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(112, 'address 12', 'Somewhere in Campanar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(113, 'address 13', 'Somewhere in Malilla', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(114, 'address 14', 'Somewhere in France', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(115, 'address 15', 'Somewhere in Birmingham', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(116, 'address 16', 'Somewhere in Scotland', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(117, 'address 17', 'Somewhere in nowhere', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(118, 'address 18', 'Somewhere over the rainbow', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(119, 'address 19', 'Somewhere in Alberic', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(120, 'address 20', 'Somewhere in Montortal', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(121, 'address 21', 'the bat cave', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 0),
|
||||
(122, 'address 22', 'NY roofs', 'Silla', 46460, 1, 1111111111, 222222222, 1, 102, 2, NULL, NULL, 0, 0),
|
||||
(123, 'address 23', 'The phone box', 'Silla', 46460, 1, 1111111111, 222222222, 1, 103, 2, NULL, NULL, 0, 0),
|
||||
(124, 'address 24', 'Stark tower', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 0),
|
||||
(125, 'address 25', 'The plastic cell', 'Silla', 46460, 1, 1111111111, 222222222, 1, 105, 2, NULL, NULL, 0, 0),
|
||||
(126, 'address 26', 'Many places', 'Silla', 46460, 1, 1111111111, 222222222, 1, 106, 2, NULL, NULL, 0, 0),
|
||||
(127, 'address 27', 'Your pocket', 'Silla', 46460, 1, 1111111111, 222222222, 1, 107, 2, NULL, NULL, 0, 0),
|
||||
(128, 'address 28', 'Cerebro', 'Silla', 46460, 1, 1111111111, 222222222, 1, 108, 2, NULL, NULL, 0, 0),
|
||||
(129, 'address 29', 'Luke Cages Bar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 110, 2, NULL, NULL, 0, 0),
|
||||
(130, 'address 30', 'Non valid address', 'Silla', 46460, 1, 1111111111, 222222222, 0, 101, 2, NULL, NULL, 0, 0),
|
||||
(131, 'Missing', 'The space', 'Silla', 46460, 1, 1111111111, 222222222, 1, 200, 2, NULL, NULL, 0, 0),
|
||||
(132, 'Trash', 'New York city', 'Silla', 46460, 1, 1111111111, 222222222, 1, 400, 2, NULL, NULL, 0, 0);
|
||||
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 1 WHERE id = 101;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 2 WHERE id = 102;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 3 WHERE id = 103;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 4 WHERE id = 104;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 5 WHERE id = 105;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 6 WHERE id = 106;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 7 WHERE id = 107;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 8 WHERE id = 108;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 9 WHERE id = 109;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 10 WHERE id = 110;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 11 WHERE id = 200;
|
||||
UPDATE `vn`.`client` SET defaultAddressFk = 12 WHERE id = 400;
|
||||
|
||||
INSERT INTO `vn`.`clientCredit`(`id`, `clientFk`, `workerFk`, `amount`, `created`)
|
||||
VALUES
|
||||
|
@ -547,7 +576,7 @@ INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `pric
|
|||
(5, 1, 2 , 'Object1 Gem1 5', 10, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -10 DAY)),
|
||||
(6, 1, 3 , 'Object1 Gem1 5', 15, 6.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -5 DAY)),
|
||||
(7, 2, 11, 'Object2 Gem2 3', 15, 1.30, 0, 0, 0, CURDATE()),
|
||||
(8, 4, 11, 'Object4 Armor2 2', 10, 3.26, 0, 0, 0, CURDATE()),
|
||||
(8, 4, 11, 'Object4 Armor2 2', 10, 3.05, 0, 0, 0, CURDATE()),
|
||||
(9, 1, 16, 'Object1 Gem1 5', 5, 9.10, 0, 0, 0, CURDATE()),
|
||||
(10, 2, 16, 'Object2 Gem2 3', 10, 1.07, 0, 0, 0, CURDATE()),
|
||||
(11, 1, 16, 'Object1 Gem1 5', 2, 9.10, 0, 0, 0, CURDATE()),
|
||||
|
@ -593,11 +622,11 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
|
|||
(5, 29, -18.72),
|
||||
(5, 39, 0.02),
|
||||
(6, 23, 6.5),
|
||||
(7, 15, 0.29),
|
||||
(7, 15, 0.2899),
|
||||
(7, 28, 5.6),
|
||||
(7, 29, -4.6),
|
||||
(7, 39, 0.01),
|
||||
(8, 15, 0.044),
|
||||
(8, 15, 0.0435),
|
||||
(8, 21, -0.004),
|
||||
(8, 28, 20.72),
|
||||
(8, 29, -19.72),
|
||||
|
@ -624,7 +653,7 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
|
|||
(12, 29, -19.72),
|
||||
(12, 37, 2),
|
||||
(12, 39, 0.01),
|
||||
(13, 15, 0.29),
|
||||
(13, 15, 0.2899),
|
||||
(13, 28, 5.6),
|
||||
(13, 29, -4.6),
|
||||
(13, 39, 0.01),
|
||||
|
@ -793,14 +822,14 @@ INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseO
|
|||
(1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000),
|
||||
(2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000),
|
||||
(3, CURDATE(), CURDATE(), 1, 2, 1, 0.00, 0.00),
|
||||
(4, DATE_ADD(CURDATE(), INTERVAL -30 DAY), DATE_ADD(CURDATE(), INTERVAL -30 DAY), 1, 2, 1, 50.00, 500);
|
||||
(4, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 50.00, 500);
|
||||
|
||||
INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `companyFk`,`ref`)
|
||||
VALUES
|
||||
( 1, 1, DATE_ADD(CURDATE(), INTERVAL -30 DAY), 1, 442, 'Movimiento 1'),
|
||||
( 2, 2, DATE_ADD(CURDATE(), INTERVAL -30 DAY), 2, 442, 'Movimiento 2'),
|
||||
( 3, 1, DATE_ADD(CURDATE(), INTERVAL -30 DAY), 3, 442, 'Movimiento 3'),
|
||||
( 4, 2, DATE_ADD(CURDATE(), INTERVAL -30 DAY), 4, 69, 'Movimiento 4');
|
||||
( 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 442, 'Movimiento 1'),
|
||||
( 2, 2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 2, 442, 'Movimiento 2'),
|
||||
( 3, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 3, 442, 'Movimiento 3'),
|
||||
( 4, 2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 69, 'Movimiento 4');
|
||||
|
||||
INSERT INTO `vn`.`agencyProvince`(`provinceFk`, `agencyFk`, `zone`, `warehouseFk`)
|
||||
VALUES
|
||||
|
@ -927,7 +956,7 @@ INSERT INTO `hedera`.`orderRowComponent`(`rowFk`, `componentFk`, `price`)
|
|||
(5, 29, -18.72),
|
||||
(5, 39, 0.02),
|
||||
(6, 23, 6.5),
|
||||
(7, 15, 0.29),
|
||||
(7, 15, 0.2899),
|
||||
(7, 28, 5.6),
|
||||
(7, 29, -4.6),
|
||||
(7, 39, 0.01),
|
||||
|
@ -1126,5 +1155,3 @@ INSERT INTO `postgresql`.`business_labour`(`business_id`, `notes`, `department_i
|
|||
VALUES
|
||||
(1, NULL, 22, 4, 0, 1, 1, 1, 1),
|
||||
(2, 'From las friday worker ownes the company 1 hour', 23, 1, 0, 1, 0, 1, 1);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -18,11 +18,11 @@ let jasmine = new Jasmine();
|
|||
let SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||
|
||||
let serviceSpecs = [
|
||||
'./db/tests/**/*[sS]pec.js'
|
||||
'./tests/**/*[sS]pec.js'
|
||||
];
|
||||
|
||||
jasmine.loadConfig({
|
||||
spec_dir: 'services',
|
||||
spec_dir: 'db',
|
||||
spec_files: serviceSpecs,
|
||||
helpers: []
|
||||
});
|
|
@ -183,6 +183,7 @@ describe('ticket ticketCreateWithUser()', () => {
|
|||
params.landed,
|
||||
params.userId
|
||||
]);
|
||||
|
||||
stmts.push(stmt);
|
||||
|
||||
let ticketAddressIndex = stmts.push(`SELECT addressFk FROM vn.ticket WHERE id = @newTicketId`) - 1;
|
||||
|
@ -191,12 +192,9 @@ describe('ticket ticketCreateWithUser()', () => {
|
|||
params.clientFk,
|
||||
]);
|
||||
let clientDefaultAddressIndex = stmts.push(stmt) - 1;
|
||||
|
||||
stmts.push('ROLLBACK');
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let result = await app.models.Ticket.rawStmt(sql);
|
||||
|
||||
let ticketAddress = result[ticketAddressIndex][0];
|
||||
let clientDefaultAddress = result[clientDefaultAddressIndex][0];
|
||||
|
|
@ -160,6 +160,16 @@ let actions = {
|
|||
.catch(done);
|
||||
},
|
||||
|
||||
waitToFocus: function(selector, done) {
|
||||
this.wait(selector)
|
||||
.evaluate_now(selector => {
|
||||
let element = document.querySelector(selector);
|
||||
element.focus();
|
||||
}, done, selector)
|
||||
.then(done)
|
||||
.catch(done);
|
||||
},
|
||||
|
||||
isVisible: function(selector, done) {
|
||||
this.wait(selector)
|
||||
.evaluate_now(elementSelector => {
|
||||
|
@ -351,6 +361,7 @@ let actions = {
|
|||
accessToSearchResult: function(searchValue, done) {
|
||||
this.write(`vn-searchbar input`, searchValue)
|
||||
.click(`vn-searchbar vn-icon[icon="search"]`)
|
||||
.wait(100)
|
||||
.waitForNumberOfElements('.searchResult', 1)
|
||||
.evaluate(() => {
|
||||
return document.querySelector('ui-view vn-card vn-table') != null;
|
||||
|
|
|
@ -31,6 +31,8 @@ export default {
|
|||
name: `${components.vnTextfield}[name="name"]`,
|
||||
taxNumber: `${components.vnTextfield}[name="fi"]`,
|
||||
socialName: `${components.vnTextfield}[name="socialName"]`,
|
||||
street: `${components.vnTextfield}[name="street"]`,
|
||||
city: `${components.vnTextfield}[name="city"]`,
|
||||
userName: `${components.vnTextfield}[name="userName"]`,
|
||||
email: `${components.vnTextfield}[name="email"]`,
|
||||
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
|
||||
|
@ -149,6 +151,7 @@ export default {
|
|||
},
|
||||
clientLog: {
|
||||
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'
|
||||
|
||||
|
@ -165,7 +168,7 @@ export default {
|
|||
},
|
||||
webPayment: {
|
||||
confirmFirstPaymentButton: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon-button[icon="done_all"]',
|
||||
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"]'
|
||||
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"][aria-hidden="false"]'
|
||||
},
|
||||
itemsIndex: {
|
||||
goBackToModuleIndexButton: `vn-ticket-descriptor a[href="#!/ticket/index"]`,
|
||||
|
@ -339,12 +342,13 @@ export default {
|
|||
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`,
|
||||
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-vertical > vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown > div > div.content > div > vn-textfield',
|
||||
firstSalePriceInput: 'vn-ticket-sale:nth-child(1) vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown vn-textfield input',
|
||||
firstSaleDiscount: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(8) > span',
|
||||
firstSaleDiscountInput: 'vn-ticket-sale:nth-child(1) vn-ticket-sale-edit-discount > div > vn-textfield > div > div > div.infix > input.ng-not-empty',
|
||||
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)`,
|
||||
|
@ -468,6 +472,17 @@ export default {
|
|||
secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimRedeliveryFk"]',
|
||||
saveDevelopmentButton: `${components.vnSubmit}`
|
||||
},
|
||||
claimAction: {
|
||||
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
||||
importTicketButton: 'vn-claim-action vn-button[label="Import ticket"]',
|
||||
secondImportableTicket: 'vn-claim-action > vn-vertical > vn-popover > div > div.content > div > vn-table > div > vn-tbody > vn-tr:nth-child(2)',
|
||||
firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
|
||||
thirdLineDestination: 'vn-claim-action vn-tr:nth-child(3) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
|
||||
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
||||
secondDeleteLine: 'vn-claim-action vn-tr:nth-child(2) vn-icon-button[icon="delete"]',
|
||||
thirdDeleteLine: 'vn-claim-action vn-tr:nth-child(3) vn-icon-button[icon="delete"]'
|
||||
|
||||
},
|
||||
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)`,
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import createNightmare from '../../helpers/nightmare';
|
||||
|
||||
describe('Claim edit basic data path', () => {
|
||||
const nightmare = createNightmare();
|
||||
|
||||
beforeAll(() => {
|
||||
nightmare
|
||||
.loginAndModule('administrative', 'claim')
|
||||
.accessToSearchResult('4')
|
||||
.accessToSection('claim.card.action');
|
||||
});
|
||||
|
||||
it('should import the claim', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.claimAction.importClaimButton)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should import the eighth ticket', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.claimAction.importTicketButton)
|
||||
.waitToClick(selectors.claimAction.secondImportableTicket)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should edit the fourth line destination field', async() => {
|
||||
const result = await nightmare
|
||||
.autocompleteSearch(selectors.claimAction.thirdLineDestination, 'Bueno')
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should delete two first lines', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.claimAction.secondDeleteLine)
|
||||
.waitToClick(selectors.claimAction.firstDeleteLine)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should refresh the view to check the remaining line is the expected one', async() => {
|
||||
const result = await nightmare
|
||||
.reloadSection('claim.card.action')
|
||||
.waitToGetProperty(`${selectors.claimAction.firstLineDestination} input`, 'value');
|
||||
|
||||
expect(result).toEqual('Bueno');
|
||||
});
|
||||
|
||||
it('should delete the current first line', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.claimAction.firstDeleteLine)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
});
|
|
@ -52,6 +52,8 @@ describe('Client create path', () => {
|
|||
const result = await nightmare
|
||||
.write(selectors.createClientView.name, 'Carol Danvers')
|
||||
.write(selectors.createClientView.socialName, 'AVG tax')
|
||||
.write(selectors.createClientView.street, 'Many places')
|
||||
.write(selectors.createClientView.city, 'Silla')
|
||||
.clearInput(selectors.createClientView.email)
|
||||
.write(selectors.createClientView.email, 'incorrect email format')
|
||||
.waitToClick(selectors.createClientView.createButton)
|
||||
|
|
|
@ -58,7 +58,7 @@ describe('Client Edit fiscalData path', () => {
|
|||
.accessToSection('client.card.fiscalData');
|
||||
});
|
||||
|
||||
it('should receive an error if VIES and EQtax are being ticked together', async() => {
|
||||
it('should edit the fiscal data', async() => {
|
||||
const result = await nightmare
|
||||
.wait(selectors.clientFiscalData.socialNameInput)
|
||||
.clearInput(selectors.clientFiscalData.socialNameInput)
|
||||
|
@ -84,9 +84,17 @@ describe('Client Edit fiscalData path', () => {
|
|||
.waitToClick(selectors.clientFiscalData.saveButton)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Cannot check VIES and Equalization Tax');
|
||||
expect(result).toEqual('Data saved!');
|
||||
}, 15000);
|
||||
|
||||
it('should propagate the Equalization tax', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.clientFiscalData.acceptPropagationButton)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Equivalent tax spreaded');
|
||||
});
|
||||
|
||||
it('should receive an error if the fiscal id contains A or B at the beginning', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.clientFiscalData.viesCheckbox)
|
||||
|
@ -108,14 +116,6 @@ describe('Client Edit fiscalData path', () => {
|
|||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should propagate the Equalization tax', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.clientFiscalData.acceptPropagationButton)
|
||||
.waitForLastSnackbar();
|
||||
|
||||
expect(result).toEqual('Equivalent tax spreaded');
|
||||
});
|
||||
|
||||
// confirm all addresses have now EQtax checked step 1
|
||||
it(`should click on the addresses button to access to the client's addresses`, async() => {
|
||||
const url = await nightmare
|
||||
|
|
|
@ -4,6 +4,8 @@ import createNightmare from '../../helpers/nightmare';
|
|||
describe('Client log path', () => {
|
||||
const nightmare = createNightmare();
|
||||
|
||||
let date;
|
||||
|
||||
beforeAll(() => {
|
||||
nightmare
|
||||
.loginAndModule('employee', 'client')
|
||||
|
@ -17,6 +19,7 @@ describe('Client log path', () => {
|
|||
.write(selectors.clientBasicData.nameInput, 'this is a test')
|
||||
.waitToClick(selectors.clientBasicData.saveButton)
|
||||
.waitForLastSnackbar();
|
||||
date = new Date();
|
||||
|
||||
expect(result).toEqual('Data saved!');
|
||||
});
|
||||
|
@ -38,9 +41,33 @@ describe('Client log path', () => {
|
|||
});
|
||||
|
||||
it('should check the current value of the last logged change', async() => {
|
||||
let lastModificationDate = await nightmare
|
||||
.waitToGetProperty(selectors.clientLog.lastModificationDate, 'innerText');
|
||||
|
||||
let lastModificationPreviousValue = await nightmare
|
||||
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
|
||||
|
||||
let lastModificationCurrentValue = await nightmare
|
||||
.waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
|
||||
|
||||
expect(lastModificationCurrentValue).toContain('this is a test');
|
||||
let month = date.getMonth() + 1;
|
||||
let day = date.getDate();
|
||||
let year = date.getFullYear();
|
||||
let hours = date.getHours();
|
||||
let minutes = date.getMinutes();
|
||||
|
||||
if (month.toString().length < 2) month = '0' + month;
|
||||
if (day.toString().length < 2) day = `0${day}`;
|
||||
if (hours.toString().length < 2) hours = '0' + hours;
|
||||
if (minutes.toString().length < 2) minutes = `0${minutes}`;
|
||||
|
||||
let today = [day, month, year].join('/');
|
||||
let time = [hours, minutes].join(':');
|
||||
|
||||
let now = [today, time].join(' ');
|
||||
|
||||
expect(lastModificationDate).toContain(now);
|
||||
expect(lastModificationPreviousValue).toEqual('name: DavidCharlesHaller');
|
||||
expect(lastModificationCurrentValue).toEqual('name: this is a test');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import createNightmare from '../../helpers/nightmare';
|
||||
|
||||
// #860 E2E client/client/src/web-payment/index.js missing fixtures for another client
|
||||
xdescribe('Client web Payment', () => {
|
||||
describe('Client web Payment', () => {
|
||||
const nightmare = createNightmare();
|
||||
|
||||
describe('as employee', () => {
|
||||
beforeAll(() => {
|
||||
nightmare
|
||||
.loginAndModule('employee', 'client')
|
||||
.accessToSearchResult('Bruce Wayne')
|
||||
.accessToSearchResult('Tony Stark')
|
||||
.accessToSection('client.card.webPayment');
|
||||
});
|
||||
|
||||
|
@ -25,13 +24,14 @@ xdescribe('Client web Payment', () => {
|
|||
beforeAll(() => {
|
||||
nightmare
|
||||
.loginAndModule('administrative', 'client')
|
||||
.accessToSearchResult('Bruce Wayne')
|
||||
.accessToSearchResult('Tony Stark')
|
||||
.accessToSection('client.card.webPayment');
|
||||
});
|
||||
|
||||
it('should be able to confirm payments', async() => {
|
||||
let exists = await nightmare
|
||||
.waitToClick(selectors.webPayment.confirmFirstPaymentButton)
|
||||
.wait(selectors.webPayment.firstPaymentConfirmed)
|
||||
.exists(selectors.webPayment.firstPaymentConfirmed);
|
||||
|
||||
expect(exists).toBeTruthy();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import createNightmare from '../../helpers/nightmare';
|
||||
|
||||
describe('Item regularize path', () => {
|
||||
// #1186 repearar e2e ticket.sale, item.regularize.
|
||||
xdescribe('Item regularize path', () => {
|
||||
const nightmare = createNightmare();
|
||||
beforeAll(() => {
|
||||
nightmare
|
||||
|
@ -176,7 +177,7 @@ describe('Item regularize path', () => {
|
|||
|
||||
it('should search for the ticket with id 23 once again', async() => {
|
||||
const result = await nightmare
|
||||
.write(selectors.ticketsIndex.searchTicketInput, 'id:23')
|
||||
.write(selectors.ticketsIndex.searchTicketInput, 'id:24')
|
||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
||||
.countElement(selectors.ticketsIndex.searchResult);
|
||||
|
@ -186,7 +187,7 @@ describe('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, '23')
|
||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, '24')
|
||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
||||
.waitForURL('/summary')
|
||||
.parsedUrl();
|
||||
|
|
|
@ -15,21 +15,21 @@ describe('Ticket List sale path', () => {
|
|||
const value = await nightmare
|
||||
.waitToGetProperty(selectors.ticketSales.firstSaleColour, 'innerText');
|
||||
|
||||
expect(value).toContain('Red');
|
||||
expect(value).toContain('Yellow');
|
||||
});
|
||||
|
||||
it('should confirm the first ticket sale contains the lenght', async() => {
|
||||
const value = await nightmare
|
||||
.waitToGetProperty(selectors.ticketSales.firstSaleText, 'innerText');
|
||||
|
||||
expect(value).toContain('3');
|
||||
expect(value).toContain('5');
|
||||
});
|
||||
|
||||
it('should confirm the first ticket sale contains the price', async() => {
|
||||
const value = await nightmare
|
||||
.waitToGetProperty(selectors.ticketSales.firstSalePrice, 'innerText');
|
||||
|
||||
expect(value).toContain('1.30');
|
||||
expect(value).toContain('2.30');
|
||||
});
|
||||
|
||||
it('should confirm the first ticket sale contains the discount', async() => {
|
||||
|
@ -43,7 +43,7 @@ describe('Ticket List sale path', () => {
|
|||
const value = await nightmare
|
||||
.waitToGetProperty(selectors.ticketSales.firstSaleImport, 'innerText');
|
||||
|
||||
expect(value).toContain('19.50');
|
||||
expect(value).toContain('23');
|
||||
});
|
||||
|
||||
it('should navigate to the catalog by pressing the new item button', async() => {
|
||||
|
|
|
@ -73,7 +73,7 @@ describe('Ticket Edit basic data path', () => {
|
|||
.wait(1900)
|
||||
.waitToGetProperty(selectors.ticketBasicData.stepTwoTotalPriceDif, 'innerText');
|
||||
|
||||
expect(result).toContain('-€187.75');
|
||||
expect(result).toContain('-€208.75');
|
||||
});
|
||||
|
||||
it(`should then click next to move on to step three`, async() => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import createNightmare from '../../helpers/nightmare';
|
||||
|
||||
// #1152 refactor ticket.sale, update price no funciona correctamente.
|
||||
// #1186 repearar e2e ticket.sale, item.regularize.
|
||||
xdescribe('Ticket Edit sale path', () => {
|
||||
const nightmare = createNightmare();
|
||||
|
||||
|
@ -138,7 +138,7 @@ xdescribe('Ticket Edit sale path', () => {
|
|||
|
||||
it('should try to add a higher quantity value and then receive an error', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.ticketSales.firstSaleQuantityClearInput)
|
||||
.waitToFocus(selectors.ticketSales.firstSaleQuantityCell)
|
||||
.write(selectors.ticketSales.firstSaleQuantity, '9\u000d')
|
||||
.waitForLastSnackbar();
|
||||
|
||||
|
@ -147,7 +147,7 @@ xdescribe('Ticket Edit sale path', () => {
|
|||
|
||||
it('should remove 1 from quantity', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.ticketSales.firstSaleQuantityClearInput)
|
||||
.waitToFocus(selectors.ticketSales.firstSaleQuantityCell)
|
||||
.write(selectors.ticketSales.firstSaleQuantity, '4\u000d')
|
||||
.waitForLastSnackbar();
|
||||
|
||||
|
@ -180,7 +180,6 @@ xdescribe('Ticket Edit sale path', () => {
|
|||
it('should update the discount', async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.ticketSales.firstSaleDiscount)
|
||||
.wait('vn-textfield[label="Discount"] > div[class="container selected"]') // a function selects the text after it's loaded
|
||||
.write(selectors.ticketSales.firstSaleDiscountInput, '50\u000d')
|
||||
// .write('body', '\u000d') // simulates enter
|
||||
.waitForLastSnackbar();
|
||||
|
@ -467,7 +466,7 @@ xdescribe('Ticket Edit sale path', () => {
|
|||
.waitToClick(selectors.ticketSales.moreMenuButton)
|
||||
.waitToClick(selectors.ticketSales.moreMenuUpdateDiscount)
|
||||
.write(selectors.ticketSales.moreMenuUpdateDiscountInput, 100)
|
||||
.write('body', '\u000d') // simulates enter
|
||||
.write('body', '\u000d')
|
||||
.waitForTextInElement(selectors.ticketSales.totalImport, '0.00')
|
||||
.waitToGetProperty(selectors.ticketSales.totalImport, 'innerText');
|
||||
|
||||
|
@ -597,16 +596,16 @@ xdescribe('Ticket Edit sale path', () => {
|
|||
describe('when state is preparation and loged as salesPerson', () => {
|
||||
it(`shouldnt be able to edit the sale price`, async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.ticketSales.firstSalePrice)
|
||||
.exists(selectors.ticketSales.firstSalePriceInput);
|
||||
.waitToClick(selectors.ticketSales.firstSaleID)
|
||||
.exists(selectors.ticketSales.firstSalePrice);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it(`shouldnt be able to edit the sale discount`, async() => {
|
||||
const result = await nightmare
|
||||
.waitToClick(selectors.ticketSales.firstSaleDiscount)
|
||||
.exists(selectors.ticketSales.firstSaleDiscountInput);
|
||||
.waitToClick(selectors.ticketSales.firstSaleID)
|
||||
.exists(selectors.ticketSales.firstSaleDiscount);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
|
|
@ -15,11 +15,10 @@ export default class Button extends Input {
|
|||
}
|
||||
|
||||
$onInit() {
|
||||
if (!this.type) {
|
||||
if (!this.type)
|
||||
this.type = 'button';
|
||||
}
|
||||
}
|
||||
}
|
||||
Button.$inject = ['$element'];
|
||||
|
||||
ngModule.component('vnButton', {
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import './searchbar.js';
|
||||
|
||||
describe('Component vnSearchbar', () => {
|
||||
let controller;
|
||||
let $element;
|
||||
let $state;
|
||||
|
||||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, _$state_) => {
|
||||
$state = _$state_;
|
||||
$element = angular.element(`<div></div>`);
|
||||
controller = $componentController('vnSearchbar', {$element, $state});
|
||||
controller.panel = 'vn-client-search-panel';
|
||||
}));
|
||||
|
||||
describe('$postLink()', () => {
|
||||
it(`should not call onStateChange() if filter is defined`, () => {
|
||||
spyOn(controller, 'onStateChange');
|
||||
controller.filter = {};
|
||||
|
||||
controller.$postLink();
|
||||
|
||||
expect(controller.onStateChange).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should call onStateChange() if filter is null`, () => {
|
||||
spyOn(controller, 'onStateChange');
|
||||
controller.filter = null;
|
||||
|
||||
controller.$postLink();
|
||||
|
||||
expect(controller.onStateChange).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter() setter', () => {
|
||||
it(`should call $state.go()`, () => {
|
||||
controller._filter = {};
|
||||
spyOn(controller.$state, 'go');
|
||||
controller.filter = {expected: 'filter'};
|
||||
|
||||
expect(controller._filter).toEqual(controller.filter);
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('.', Object({q: '{"expected":"filter"}'}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('openPanel()', () => {
|
||||
it(`should do nothing if the event is prevented`, () => {
|
||||
let event = {
|
||||
defaultPrevented: true,
|
||||
preventDefault: jasmine.createSpy('preventDefault')
|
||||
};
|
||||
controller.openPanel(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getObjectFromString()', () => {
|
||||
it(`should return a formated object based on the string received for basic search`, () => {
|
||||
let result = controller.getObjectFromString('Bruce Wayne');
|
||||
|
||||
expect(result).toEqual({search: 'Bruce Wayne'});
|
||||
});
|
||||
|
||||
it(`should return a formated object based on the string received for advanced search`, () => {
|
||||
let result = controller.getObjectFromString('id:101 name:(Bruce Wayne)');
|
||||
|
||||
expect(result).toEqual({id: '101', name: 'Bruce Wayne'});
|
||||
});
|
||||
|
||||
it(`should format the object grouping any unmatched part of the instring of the string to the search property`, () => {
|
||||
let string = 'I am the search id:101 name:(Bruce Wayne) concatenated value';
|
||||
let result = controller.getObjectFromString(string);
|
||||
|
||||
expect(result).toEqual({
|
||||
id: '101',
|
||||
name: 'Bruce Wayne',
|
||||
search: 'I am the search concatenated value'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,2 @@
|
|||
<vn-horizontal class="text-container">
|
||||
<span class="text" ng-transclude="text" vn-one></span>
|
||||
</vn-horizontal>
|
||||
<div class="field"></div>
|
||||
<span class="text" ng-transclude="text"></span>
|
|
@ -1,42 +1,67 @@
|
|||
import ngModule from '../../module';
|
||||
import Component from '../../lib/component';
|
||||
import {focus} from '../../directives/focus';
|
||||
import './style.scss';
|
||||
|
||||
export default class Controller extends Component {
|
||||
constructor($element, $scope, $transclude) {
|
||||
constructor($element, $scope, $transclude, $timeout) {
|
||||
super($element, $scope);
|
||||
this.$timeout = $timeout;
|
||||
let element = $element[0];
|
||||
element.tabIndex = 0;
|
||||
|
||||
element.addEventListener('focus', () => {
|
||||
if (this.field || this.disabled) return;
|
||||
|
||||
$transclude((tClone, tScope) => {
|
||||
this.field = tClone;
|
||||
this.tScope = tScope;
|
||||
this.element.querySelector('.field').appendChild(this.field[0]);
|
||||
element.tabIndex = -1;
|
||||
this.timer = $timeout(() => {
|
||||
this.timer = null;
|
||||
focus(this.field[0]);
|
||||
});
|
||||
}, null, 'field');
|
||||
element.classList.add('selected');
|
||||
});
|
||||
|
||||
element.addEventListener('focusout', event => {
|
||||
if (!this.field || this.disabled) return;
|
||||
// this.destroyTimer();
|
||||
this.lastEvent = event;
|
||||
let target = event.relatedTarget;
|
||||
while (target && target.parentNode != element)
|
||||
while (target && target != element)
|
||||
target = target.parentNode;
|
||||
|
||||
if (!target) {
|
||||
this.tScope.$destroy();
|
||||
this.field.remove();
|
||||
this.field = null;
|
||||
element.classList.remove('selected');
|
||||
element.tabIndex = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
destroyTimer() {
|
||||
if (this.timer) {
|
||||
this.$timeout.cancel(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope', '$transclude'];
|
||||
$onDestroy() {
|
||||
this.destroyTimer();
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$scope', '$transclude', '$timeout'];
|
||||
|
||||
ngModule.component('vnTdEditable', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
disabled: '<?'
|
||||
},
|
||||
transclude: {
|
||||
text: 'text',
|
||||
field: '?field'
|
||||
|
|
|
@ -1,16 +1,54 @@
|
|||
@import "variables";
|
||||
|
||||
vn-td-editable {
|
||||
text {
|
||||
cursor: pointer;
|
||||
& > div.text-container{
|
||||
width: 100%;
|
||||
display: block
|
||||
}
|
||||
|
||||
&.selected {
|
||||
& > .text-container{
|
||||
outline: none;
|
||||
position: relative;
|
||||
&:not([disabled="true"]) {
|
||||
cursor: initial;
|
||||
|
||||
|
||||
text:hover::after {
|
||||
font-family: 'salixfont';
|
||||
float: right;
|
||||
content: '\e900';
|
||||
display: block
|
||||
}
|
||||
}
|
||||
&.selected > .text {
|
||||
visibility: hidden;
|
||||
}
|
||||
& > .field {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: .6em;
|
||||
overflow: visible;
|
||||
|
||||
vn-icon {
|
||||
font-size: 1em;
|
||||
& > field {
|
||||
flex: 1;
|
||||
background-color: $color-bg-panel;
|
||||
padding: .5em;
|
||||
box-shadow: 0 0 .4em rgba(0, 0, 0, .2);
|
||||
border-radius: .1em;
|
||||
min-width: 6em;
|
||||
|
||||
& > * {
|
||||
width: 100%;
|
||||
max-width: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.selected > .field {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -18,18 +18,15 @@ export default class Textfield extends Input {
|
|||
if (!this.oldValue)
|
||||
this.saveOldValue();
|
||||
});
|
||||
|
||||
this.input.addEventListener('keyup', e => {
|
||||
if (e.key == 'Escape') {
|
||||
if (e.defaultPrevented || e.key != 'Escape')
|
||||
return;
|
||||
|
||||
this.value = this.oldValue;
|
||||
this.cancelled = true;
|
||||
e.stopPropagation();
|
||||
}
|
||||
if (e.key == 'Escape' || e.key == 'Enter')
|
||||
this.input.blur();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.input.addEventListener('blur', () => {
|
||||
this.input.addEventListener('change', e => {
|
||||
if (this.onChange && !this.cancelled && (this.oldValue != this.value)) {
|
||||
this.onChange();
|
||||
this.saveOldValue();
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
/**
|
||||
* Sets the focus and selects the text on the input.
|
||||
*
|
||||
* @return {Object} The directive
|
||||
*/
|
||||
export function directive() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, $element, $attrs) {
|
||||
$scope.$watch('', function() {
|
||||
let input = $element[0];
|
||||
export function focus(input) {
|
||||
let selector = 'input, textarea, button, submit';
|
||||
|
||||
if (!input.matches(selector))
|
||||
|
@ -25,7 +15,18 @@ export function directive() {
|
|||
|
||||
if (input.select)
|
||||
input.select();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the focus and selects the text on the input.
|
||||
*
|
||||
* @return {Object} The directive
|
||||
*/
|
||||
export function directive() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, $element) {
|
||||
$scope.$watch('', () => focus($element[0]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -17,6 +17,11 @@ describe('Component vnLeftMenu', () => {
|
|||
];
|
||||
}));
|
||||
|
||||
it(`should set items in the controller for the left menu`, () => {
|
||||
expect(controller.items.length).toEqual(3);
|
||||
expect(controller.items[2].state).toEqual('client.card.summary');
|
||||
});
|
||||
|
||||
describe('depth() setter', () => {
|
||||
it(`should set depth property and call activateItem()`, () => {
|
||||
spyOn(controller, 'activateItem');
|
||||
|
|
|
@ -355,7 +355,7 @@ async function docker() {
|
|||
let d = new Date();
|
||||
let pad = v => v < 10 ? '0' + v : v;
|
||||
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||
await execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./services/db`);
|
||||
await execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./db`);
|
||||
|
||||
let dockerArgs = `--name ${containerId} -p 3306:${dbConf.port}`;
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@ module.exports = function(Self) {
|
|||
});
|
||||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
let options = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
options.transaction = ctx.options.transaction;
|
||||
|
||||
let oldInstance;
|
||||
let oldInstanceFk;
|
||||
let newInstance;
|
||||
|
@ -22,7 +26,7 @@ module.exports = function(Self) {
|
|||
oldInstance = await fkToValue(oldInstanceFk, ctx);
|
||||
if (ctx.where && !ctx.currentInstance) {
|
||||
let fields = Object.keys(ctx.data);
|
||||
ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields});
|
||||
ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields}, options);
|
||||
}
|
||||
}
|
||||
if (ctx.isNewInstance)
|
||||
|
@ -33,10 +37,14 @@ module.exports = function(Self) {
|
|||
});
|
||||
|
||||
Self.observe('before delete', async function(ctx) {
|
||||
let options = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
options.transaction = ctx.options.transaction;
|
||||
|
||||
if (ctx.where) {
|
||||
let affectedModel = ctx.Model.definition.name;
|
||||
let definition = ctx.Model.definition;
|
||||
let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where});
|
||||
let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where}, options);
|
||||
let relation = definition.settings.log.relation;
|
||||
|
||||
if (relation) {
|
||||
|
@ -61,6 +69,10 @@ module.exports = function(Self) {
|
|||
});
|
||||
|
||||
async function logDeletedInstances(ctx, loopBackContext) {
|
||||
let options = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
options.transaction = ctx.options.transaction;
|
||||
|
||||
ctx.hookState.oldInstance.forEach(async instance => {
|
||||
let userFk;
|
||||
if (loopBackContext)
|
||||
|
@ -80,16 +92,16 @@ module.exports = function(Self) {
|
|||
newInstance: {}
|
||||
};
|
||||
|
||||
let transaction = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
transaction = ctx.options.transaction;
|
||||
|
||||
let logModel = definition.settings.log.model;
|
||||
await ctx.Model.app.models[logModel].create(logRecord, transaction);
|
||||
await ctx.Model.app.models[logModel].create(logRecord, options);
|
||||
});
|
||||
}
|
||||
|
||||
async function fkToValue(instance, ctx) {
|
||||
let options = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
options.transaction = ctx.options.transaction;
|
||||
|
||||
let cleanInstance = JSON.parse(JSON.stringify(instance));
|
||||
let result = {};
|
||||
for (let key in cleanInstance) {
|
||||
|
@ -98,8 +110,11 @@ module.exports = function(Self) {
|
|||
for (let key1 in ctx.Model.relations) {
|
||||
let val1 = ctx.Model.relations[key1];
|
||||
if (val1.keyFrom == key && key != 'id') {
|
||||
let recordSet = await val1.modelTo.findById(val);
|
||||
val = recordSet.name; // FIXME preparar todos los modelos con campo name
|
||||
let recordSet = await ctx.Model.app.models[val1.modelTo.modelName].findById(val, options);
|
||||
let definition = val1.modelTo.definition;
|
||||
let changedModelValue = definition.settings.log && definition.settings.log.changedModelValue;
|
||||
|
||||
val = (changedModelValue && recordSet && recordSet[changedModelValue]) || (recordSet && recordSet.id) || val; // FIXME preparar todos los modelos con campo name
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +124,10 @@ module.exports = function(Self) {
|
|||
}
|
||||
|
||||
async function logInModel(ctx, loopBackContext) {
|
||||
let options = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
options.transaction = ctx.options.transaction;
|
||||
|
||||
let definition = ctx.Model.definition;
|
||||
let primaryKey;
|
||||
for (let property in definition.properties) {
|
||||
|
@ -143,10 +162,11 @@ module.exports = function(Self) {
|
|||
|
||||
// Sets the changedModelValue to save and the instances changed in case its an updateAll
|
||||
let changedModelValue = definition.settings.log.changedModelValue;
|
||||
let where;
|
||||
if (changedModelValue && (!ctx.instance || !ctx.instance[changedModelValue])) {
|
||||
var where = [];
|
||||
changedModelId = [];
|
||||
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]});
|
||||
where = [];
|
||||
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]}, options);
|
||||
changedInstances.forEach(element => {
|
||||
where.push(element[changedModelValue]);
|
||||
changedModelId.push(element.id);
|
||||
|
@ -185,11 +205,7 @@ module.exports = function(Self) {
|
|||
|
||||
let logModel = definition.settings.log.model;
|
||||
|
||||
let transaction = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
transaction = ctx.options.transaction;
|
||||
|
||||
await ctx.Model.app.models[logModel].create(logsToSave, transaction);
|
||||
await ctx.Model.app.models[logModel].create(logsToSave, options);
|
||||
}
|
||||
|
||||
// this function retuns all the instances changed in case this is an updateAll
|
||||
|
|
|
@ -37,5 +37,7 @@
|
|||
"Barcode must be unique": "Barcode must be unique",
|
||||
"You don't have enough privileges to do that": "You don't have enough privileges to do that",
|
||||
"You can't create a ticket for a frozen client": "You can't create a ticket for a frozen client",
|
||||
"can't be blank": "can't be blank"
|
||||
"can't be blank": "can't be blank",
|
||||
"Street cannot be empty": "Street cannot be empty",
|
||||
"City cannot be empty": "City cannot be empty"
|
||||
}
|
|
@ -68,5 +68,9 @@
|
|||
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
|
||||
"ORDER_EMPTY": "Cesta vacía",
|
||||
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
|
||||
"You can't create a ticket for a client that has a debt": "No puedes crear un ticket para un client con deuda"
|
||||
"You can't create a ticket for a client that has a debt": "No puedes crear un ticket para un client con deuda",
|
||||
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
|
||||
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
|
||||
"Street cannot be empty": "Street cannot be empty",
|
||||
"City cannot be empty": "City cannot be empty"
|
||||
}
|
|
@ -3,10 +3,12 @@ const app = require('vn-loopback/server/server');
|
|||
describe('Claim importTicketSales()', () => {
|
||||
let claimEnds;
|
||||
|
||||
afterAll(async () => {
|
||||
afterAll(async done => {
|
||||
claimEnds.forEach(async line => {
|
||||
await line.destroy();
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should import sales to a claim actions from an specific ticket', async() => {
|
||||
|
|
|
@ -4,8 +4,10 @@ describe('Claim Create', () => {
|
|||
let newDate = new Date();
|
||||
let createdClaimFk;
|
||||
|
||||
afterAll(async () => {
|
||||
afterAll(async done => {
|
||||
await app.models.Claim.destroyById(createdClaimFk);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
let newClaim = {
|
||||
|
|
|
@ -9,7 +9,7 @@ xdescribe('regularizeClaim()', () => {
|
|||
let claimEnds = [];
|
||||
let trashTicket;
|
||||
|
||||
afterAll(async () => {
|
||||
afterAll(async done => {
|
||||
let claim = await app.models.Claim.findById(claimFk);
|
||||
await claim.updateAttributes({claimStateFk: pendentState});
|
||||
await app.models.Ticket.destroyById(trashTicket.id);
|
||||
|
@ -17,6 +17,8 @@ xdescribe('regularizeClaim()', () => {
|
|||
claimEnds.forEach(async line => {
|
||||
await line.destroy();
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should change claim state to resolved', async() => {
|
||||
|
|
|
@ -14,12 +14,16 @@ describe('Update Claim', () => {
|
|||
observation: 'observation'
|
||||
};
|
||||
|
||||
beforeAll(async() => {
|
||||
beforeAll(async done => {
|
||||
newInstance = await app.models.Claim.create(original);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.Claim.destroyById(newInstance.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should throw error if isSaleAssistant is false and try to modify a forbidden field', async() => {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
module.exports = function(Self) {
|
||||
Self.remoteMethod('createDefaultAddress', {
|
||||
description: 'Creates both client and its web account',
|
||||
accepts: {
|
||||
arg: 'data',
|
||||
type: 'object',
|
||||
http: {source: 'body'}
|
||||
},
|
||||
returns: {
|
||||
root: true,
|
||||
type: 'Object'
|
||||
},
|
||||
http: {
|
||||
verb: 'post',
|
||||
path: '/createDefaultAddress'
|
||||
}
|
||||
});
|
||||
|
||||
Self.createDefaultAddress = async data => {
|
||||
const Address = Self.app.models.Address;
|
||||
const Client = Self.app.models.Client;
|
||||
const transaction = await Address.beginTransaction({});
|
||||
|
||||
try {
|
||||
let address = data.address;
|
||||
let newAddress = await Address.create(address, {transaction});
|
||||
let client = await Client.findById(address.clientFk, {transaction});
|
||||
|
||||
if (data.isDefaultAddress) {
|
||||
await client.updateAttributes({
|
||||
defaultAddressFk: newAddress.id
|
||||
}, {transaction});
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
return newAddress;
|
||||
} catch (e) {
|
||||
await transaction.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('Address createDefaultAddress', () => {
|
||||
let address;
|
||||
let client;
|
||||
|
||||
afterAll(async done => {
|
||||
await client.updateAttributes({defaultAddressFk: 1});
|
||||
await address.destroy();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should verify that client defaultAddressFk is untainted', async() => {
|
||||
client = await app.models.Client.findById(101);
|
||||
|
||||
expect(client.defaultAddressFk).toEqual(1);
|
||||
});
|
||||
|
||||
it('should create a new address and set as a client default address', async() => {
|
||||
let data = {
|
||||
address: {
|
||||
clientFk: 101,
|
||||
nickname: 'My address',
|
||||
street: 'Wall Street',
|
||||
city: 'New York',
|
||||
|
||||
},
|
||||
isDefaultAddress: true
|
||||
};
|
||||
|
||||
address = await app.models.Address.createDefaultAddress(data);
|
||||
client = await app.models.Client.findById(101);
|
||||
|
||||
expect(client.defaultAddressFk).toEqual(address.id);
|
||||
});
|
||||
});
|
|
@ -23,13 +23,13 @@ module.exports = function(Self) {
|
|||
email: firstEmail,
|
||||
password: parseInt(Math.random() * 100000000000000)
|
||||
};
|
||||
let Account = Self.app.models.Account;
|
||||
|
||||
let transaction = await Account.beginTransaction({});
|
||||
const Account = Self.app.models.Account;
|
||||
const Address = Self.app.models.Address;
|
||||
const transaction = await Account.beginTransaction({});
|
||||
|
||||
try {
|
||||
let account = await Account.create(user, {transaction});
|
||||
let client = {
|
||||
let client = await Self.create({
|
||||
id: account.id,
|
||||
name: data.name,
|
||||
fi: data.fi,
|
||||
|
@ -42,10 +42,26 @@ module.exports = function(Self) {
|
|||
provinceFk: data.provinceFk,
|
||||
countryFk: data.countryFk,
|
||||
isEqualizated: data.isEqualizated
|
||||
};
|
||||
newClient = await Self.create(client);
|
||||
}, {transaction});
|
||||
|
||||
|
||||
let address = await Address.create({
|
||||
clientFk: client.id,
|
||||
nickname: client.name,
|
||||
city: client.city,
|
||||
street: client.street,
|
||||
postalCode: client.postcode,
|
||||
provinceFk: client.provinceFk,
|
||||
isEqualizated: client.isEqualizated,
|
||||
isActive: true
|
||||
}, {transaction});
|
||||
|
||||
await client.updateAttributes({
|
||||
defaultAddressFk: address.id
|
||||
}, {transaction});
|
||||
|
||||
await transaction.commit();
|
||||
return newClient;
|
||||
return client;
|
||||
} catch (e) {
|
||||
await transaction.rollback();
|
||||
throw e;
|
||||
|
|
|
@ -8,9 +8,11 @@ describe('Client addressesPropagateRe', () => {
|
|||
await client.updateAttributes({hasToInvoiceByAddress: true});
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.Address.update({clientFk: 101}, {isEqualizated: false});
|
||||
await client.updateAttributes({hasToInvoiceByAddress: true});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => {
|
||||
|
|
|
@ -3,9 +3,11 @@ const app = require('vn-loopback/server/server');
|
|||
describe('Client confirmTransaction', () => {
|
||||
const transactionId = 2;
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.Client.rawSql(`
|
||||
CALL hedera.tpvTransactionUndo(?)`, [transactionId]);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should call confirmTransaction() method to mark transaction as confirmed', async() => {
|
||||
|
|
|
@ -3,22 +3,16 @@ const app = require('vn-loopback/server/server');
|
|||
describe('Client Create', () => {
|
||||
const clientName = 'Wade';
|
||||
const AccountName = 'Deadpool';
|
||||
/* beforeAll(async() => {
|
||||
let address = await app.models.Address.findOne({where: {nickname: clientName}});
|
||||
let client = await app.models.Client.findOne({where: {name: clientName}});
|
||||
let account = await app.models.Account.findOne({where: {name: AccountName}});
|
||||
await app.models.Address.destroyById(address.id);
|
||||
await app.models.Client.destroyById(client.id);
|
||||
await app.models.Account.destroyById(account.id);
|
||||
}); */
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
let address = await app.models.Address.findOne({where: {nickname: clientName}});
|
||||
let client = await app.models.Client.findOne({where: {name: clientName}});
|
||||
let account = await app.models.Account.findOne({where: {name: AccountName}});
|
||||
await app.models.Address.destroyById(address.id);
|
||||
await app.models.Client.destroyById(client.id);
|
||||
await app.models.Account.destroyById(account.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
let newAccount = {
|
||||
|
@ -26,7 +20,9 @@ describe('Client Create', () => {
|
|||
email: 'Deadpool@marvel.com',
|
||||
fi: '16195279J',
|
||||
name: 'Wade',
|
||||
socialName: 'Deadpool Marvel'
|
||||
socialName: 'Deadpool Marvel',
|
||||
street: 'Wall Street',
|
||||
city: 'New York'
|
||||
};
|
||||
|
||||
it(`should not find Deadpool as he's not created yet`, async() => {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('Client updateFiscalData', () => {
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
let ctxOfAdmin = {req: {accessToken: {userId: 5}}};
|
||||
let validparams = {postcode: 46460};
|
||||
let idWithDataChecked = 101;
|
||||
|
||||
await app.models.Client.updateFiscalData(ctxOfAdmin, validparams, idWithDataChecked);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should return an error if the user is not administrative and the isTaxDataChecked value is true', async() => {
|
||||
|
|
|
@ -78,9 +78,8 @@ module.exports = Self => {
|
|||
}
|
||||
},
|
||||
{
|
||||
relation: 'addresses',
|
||||
relation: 'defaultAddress',
|
||||
scope: {
|
||||
where: {isDefaultAddress: true},
|
||||
fields: ['nickname', 'street', 'city', 'postalCode']
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,8 +3,10 @@ const app = require('vn-loopback/server/server');
|
|||
describe('Client createWithInsurance', () => {
|
||||
let classificationId;
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.CreditClassification.destroyById(classificationId);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should verify the classifications and insurances are untainted', async() => {
|
||||
|
@ -24,7 +26,7 @@ describe('Client createWithInsurance', () => {
|
|||
error = e;
|
||||
});
|
||||
|
||||
expect(error.toString()).toBe("Error: ER_BAD_NULL_ERROR: Column 'client' cannot be null");
|
||||
expect(error.toString()).toBe('Error: ER_BAD_NULL_ERROR: Column \'client\' cannot be null');
|
||||
|
||||
let classifications = await app.models.CreditClassification.find();
|
||||
let insurances = await app.models.CreditInsurance.find();
|
||||
|
|
|
@ -3,13 +3,8 @@ let getFinalState = require('vn-loopback/util/hook').getFinalState;
|
|||
let isMultiple = require('vn-loopback/util/hook').isMultiple;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.validate('isDefaultAddress', isActive,
|
||||
{message: 'Unable to default a disabled consignee'}
|
||||
);
|
||||
|
||||
function isActive(err) {
|
||||
if (!this.isActive && this.isDefaultAddress) err();
|
||||
}
|
||||
// Methods
|
||||
require('../methods/address/createDefaultAddress')(Self);
|
||||
|
||||
Self.validateAsync('isEqualizated', cannotHaveET, {
|
||||
message: 'Cannot check Equalization Tax in this NIF/CIF'
|
||||
|
@ -50,31 +45,26 @@ module.exports = Self => {
|
|||
// Helpers
|
||||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
const Client = Self.app.models.Client;
|
||||
|
||||
if (isMultiple(ctx)) return;
|
||||
|
||||
let transaction = {};
|
||||
if (ctx.options && ctx.options.transaction)
|
||||
transaction = ctx.options.transaction;
|
||||
|
||||
let changes = ctx.data || ctx.instance;
|
||||
let finalState = getFinalState(ctx);
|
||||
|
||||
if (changes.isActive == false && finalState.isDefaultAddress)
|
||||
const client = await Client.findById(finalState.clientFk, {
|
||||
fields: ['isEqualizated', 'defaultAddressFk']
|
||||
}, {transaction});
|
||||
|
||||
if (changes.isActive == false && client.defaultAddressFk === finalState.id)
|
||||
throw new UserError('The default consignee can not be unchecked');
|
||||
|
||||
if (changes.isDefaultAddress == true && finalState.isActive != false) {
|
||||
let filter = {
|
||||
clientFk: finalState.clientFk,
|
||||
isDefaultAddress: {neq: false}
|
||||
};
|
||||
await Self.updateAll(filter, {isDefaultAddress: false});
|
||||
}
|
||||
|
||||
if (ctx.isNewInstance == true) {
|
||||
let filter = {
|
||||
where: {
|
||||
id: ctx.instance.clientFk
|
||||
},
|
||||
fields: ['isEqualizated']
|
||||
};
|
||||
let findOneResponse = await Self.app.models.Client.findOne(filter);
|
||||
ctx.instance.isEqualizated = findOneResponse.isEqualizated;
|
||||
}
|
||||
// Propagate client isEqualizated to all addresses
|
||||
if (ctx.isNewInstance == true)
|
||||
ctx.instance.isEqualizated = client.isEqualizated;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -42,9 +42,6 @@
|
|||
"isActive": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isDefaultAddress": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "Number"
|
||||
},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
let request = require('request-promise-native');
|
||||
let UserError = require('vn-loopback/util/user-error');
|
||||
let getFinalState = require('vn-loopback/util/hook').getFinalState;
|
||||
let isMultiple = require('vn-loopback/util/hook').isMultiple;
|
||||
|
@ -21,6 +22,14 @@ module.exports = Self => {
|
|||
|
||||
// Validations
|
||||
|
||||
Self.validatesPresenceOf('street', {
|
||||
message: 'Street cannot be empty'
|
||||
});
|
||||
|
||||
Self.validatesPresenceOf('city', {
|
||||
message: 'City cannot be empty'
|
||||
});
|
||||
|
||||
Self.validatesUniquenessOf('fi', {
|
||||
message: 'TIN must be unique'
|
||||
});
|
||||
|
@ -90,15 +99,6 @@ module.exports = Self => {
|
|||
err();
|
||||
}
|
||||
|
||||
Self.validate('isVies', hasEquTax, {
|
||||
message: 'Cannot check VIES and Equalization Tax'
|
||||
});
|
||||
|
||||
function hasEquTax(err) {
|
||||
if (this.isVies && this.isEqualizated)
|
||||
err();
|
||||
}
|
||||
|
||||
Self.validate('isEqualizated', cannotHaveET, {
|
||||
message: 'Cannot check Equalization Tax in this NIF/CIF'
|
||||
});
|
||||
|
@ -133,6 +133,19 @@ module.exports = Self => {
|
|||
done();
|
||||
}
|
||||
|
||||
Self.validateAsync('defaultAddressFk', isActive,
|
||||
{message: 'Unable to default a disabled consignee'}
|
||||
);
|
||||
|
||||
async function isActive(err, done) {
|
||||
if (!this.defaultAddressFk)
|
||||
return done();
|
||||
|
||||
const address = await Self.app.models.Address.findById(this.defaultAddressFk);
|
||||
if (address && !address.isActive) err();
|
||||
done();
|
||||
}
|
||||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
let changes = ctx.data || ctx.instance;
|
||||
let orgData = ctx.currentInstance;
|
||||
|
@ -167,6 +180,41 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.observe('after save', async ctx => {
|
||||
if (ctx.isNewInstance) return;
|
||||
|
||||
const hookState = ctx.hookState;
|
||||
const newInstance = hookState.newInstance;
|
||||
const oldInstance = hookState.oldInstance;
|
||||
const instance = ctx.instance;
|
||||
|
||||
const payMethodChanged = oldInstance.payMethodFk != newInstance.payMethodFk;
|
||||
const ibanChanged = oldInstance.iban != newInstance.iban;
|
||||
const dueDayChanged = oldInstance.dueDay != newInstance.dueDay;
|
||||
|
||||
if (payMethodChanged || ibanChanged || dueDayChanged) {
|
||||
const message = `La forma de pago del cliente con id ${instance.id} ha cambiado`;
|
||||
const salesPersonFk = instance.salesPersonFk;
|
||||
|
||||
if (salesPersonFk) {
|
||||
await Self.app.models.Message.send(ctx, {
|
||||
recipientFk: salesPersonFk,
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: 'http://127.0.0.1:3000/api/email/payment-update',
|
||||
body: {
|
||||
clientFk: instance.id
|
||||
},
|
||||
json: true
|
||||
};
|
||||
await request(options);
|
||||
}
|
||||
});
|
||||
|
||||
async function validateCreditChange(ctx, finalState) {
|
||||
let models = Self.app.models;
|
||||
let userId = ctx.options.accessToken.userId;
|
||||
|
|
|
@ -31,10 +31,12 @@
|
|||
"type": "string"
|
||||
},
|
||||
"street": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"postcode": {
|
||||
"type": "string"
|
||||
|
@ -114,7 +116,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "date"
|
||||
"type": "Date"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -3,14 +3,13 @@ const app = require('vn-loopback/server/server');
|
|||
describe('loopback model address', () => {
|
||||
let createdAddressId;
|
||||
|
||||
afterAll(async() => {
|
||||
let address = await app.models.Address.findById(1);
|
||||
afterAll(async done => {
|
||||
let client = await app.models.Client.findById(101);
|
||||
|
||||
await address.updateAttribute('isDefaultAddress', true);
|
||||
await app.models.Address.destroyById(createdAddressId);
|
||||
|
||||
await client.updateAttribute('isEqualizated', false);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
describe('observe()', () => {
|
||||
|
@ -28,24 +27,6 @@ describe('loopback model address', () => {
|
|||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should set isDefaultAddress to false of all the addresses for a given client', async() => {
|
||||
let previousDefaultAddress = await app.models.Address.findById(1);
|
||||
|
||||
expect(previousDefaultAddress.isDefaultAddress).toBeTruthy();
|
||||
|
||||
let address = await app.models.Address.findById(121);
|
||||
|
||||
expect(address.isDefaultAddress).toBeFalsy();
|
||||
|
||||
let defaultAddress = await address.updateAttribute('isDefaultAddress', true);
|
||||
|
||||
expect(defaultAddress.isDefaultAddress).toBeTruthy();
|
||||
|
||||
let noLongerDefaultAddress = await app.models.Address.findById(1);
|
||||
|
||||
expect(noLongerDefaultAddress.isDefaultAddress).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => {
|
||||
let client = await app.models.Client.findById(101);
|
||||
|
||||
|
@ -58,7 +39,6 @@ describe('loopback model address', () => {
|
|||
agencyModeFk: 5,
|
||||
city: 'here',
|
||||
isActive: true,
|
||||
isDefaultAddress: false,
|
||||
mobile: '555555555',
|
||||
nickname: 'Test address',
|
||||
phone: '555555555',
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Addresses"
|
||||
url="/client/api/Addresses/createDefaultAddress"
|
||||
id-field="id"
|
||||
data="$ctrl.address"
|
||||
data="$ctrl.data"
|
||||
save="post"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submitGo('client.card.address.index')" compact>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" compact>
|
||||
<vn-card pad-large>
|
||||
<vn-horizontal pad-small-v>
|
||||
<vn-check vn-one label="Default" field="$ctrl.address.isDefaultAddress"></vn-check>
|
||||
<vn-check vn-one label="Default" field="$ctrl.data.isDefaultAddress"></vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Consignee" field="$ctrl.address.nickname" vn-focus></vn-textfield>
|
||||
|
|
|
@ -1,16 +1,35 @@
|
|||
import ngModule from '../../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($state) {
|
||||
this.address = {
|
||||
constructor($scope, $state) {
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.data = {
|
||||
address: {
|
||||
clientFk: parseInt($state.params.id),
|
||||
isActive: true
|
||||
},
|
||||
isDefaultAddress: false
|
||||
};
|
||||
this.address = this.data.address;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.$scope.watcher.submit().then(res => {
|
||||
if (res.data && this.data.isDefaultAddress)
|
||||
this.client.defaultAddressFk = res.data.id;
|
||||
|
||||
this.$state.go('client.card.address.index');
|
||||
});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$state'];
|
||||
|
||||
Controller.$inject = ['$scope', '$state'];
|
||||
|
||||
ngModule.component('vnClientAddressCreate', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import './index';
|
||||
import watcher from 'core/mocks/watcher';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientAddressCreate', () => {
|
||||
|
@ -13,11 +14,40 @@ describe('Client', () => {
|
|||
$state = _$state_;
|
||||
$state.params.id = '1234';
|
||||
controller = $componentController('vnClientAddressCreate', {$state});
|
||||
controller.$scope.watcher = watcher;
|
||||
controller.$scope.watcher.submit = () => {
|
||||
return {
|
||||
then: callback => {
|
||||
callback({data: {id: 124}});
|
||||
}
|
||||
};
|
||||
};
|
||||
controller.client = {id: 101, defaultAddressFk: 121};
|
||||
}));
|
||||
|
||||
it('should define and set address property', () => {
|
||||
expect(controller.address.clientFk).toBe(1234);
|
||||
expect(controller.address.isActive).toBe(true);
|
||||
expect(controller.data.address.clientFk).toBe(1234);
|
||||
expect(controller.data.address.isActive).toBe(true);
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it('should perform a PATCH and not set value to defaultAddressFk property', () => {
|
||||
spyOn(controller.$state, 'go');
|
||||
controller.data.isDefaultAddress = false;
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.client.defaultAddressFk).toEqual(121);
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
|
||||
});
|
||||
|
||||
it('should perform a PATCH and set a value to defaultAddressFk property', () => {
|
||||
spyOn(controller.$state, 'go');
|
||||
controller.data.isDefaultAddress = true;
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.client.defaultAddressFk).toEqual(124);
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,24 +3,27 @@
|
|||
url="/client/api/Addresses"
|
||||
filter="::$ctrl.filter"
|
||||
link="{clientFk: $ctrl.$stateParams.id}"
|
||||
data="addresses"
|
||||
data="$ctrl.addresses"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<div compact>
|
||||
<vn-card pad-large>
|
||||
<vn-horizontal
|
||||
ng-repeat="address in addresses"
|
||||
ng-repeat="address in $ctrl.addresses"
|
||||
class="pad-medium-top"
|
||||
style="align-items: center;">
|
||||
<vn-one
|
||||
border-radius
|
||||
class="pad-small border-solid"
|
||||
ng-class="{'item-hightlight': address.isDefaultAddress, 'item-disabled': !address.isActive && !address.isDefaultAddress}">
|
||||
ng-class="{
|
||||
'item-hightlight': $ctrl.isDefaultAddress(address),
|
||||
'item-disabled': !address.isActive && !$ctrl.isDefaultAddress(address)
|
||||
}">
|
||||
<vn-horizontal style="align-items: center;">
|
||||
<vn-none pad-medium-h>
|
||||
<vn-icon-button
|
||||
icon="star"
|
||||
ng-if="address.isDefaultAddress">
|
||||
ng-if="$ctrl.isDefaultAddress(address)">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
ng-if="!address.isActive"
|
||||
|
@ -28,7 +31,7 @@
|
|||
vn-tooltip="Active first to set as default">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
ng-if="address.isActive && !address.isDefaultAddress"
|
||||
ng-if="address.isActive && !$ctrl.isDefaultAddress(address)"
|
||||
icon="star_border"
|
||||
vn-tooltip="Set as default"
|
||||
ng-click="$ctrl.setDefault(address)">
|
||||
|
|
|
@ -9,22 +9,60 @@ class Controller {
|
|||
include: {
|
||||
observations: 'observationType'
|
||||
},
|
||||
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC']
|
||||
order: ['isActive DESC', 'nickname ASC']
|
||||
};
|
||||
}
|
||||
|
||||
setDefault(address) {
|
||||
if (address.isActive) {
|
||||
let params = {isDefaultAddress: true};
|
||||
this.$http.patch(`/client/api/Addresses/${address.id}`, params).then(
|
||||
() => this.$scope.model.refresh()
|
||||
);
|
||||
get client() {
|
||||
return this._client;
|
||||
}
|
||||
|
||||
set client(value) {
|
||||
this._client = value;
|
||||
this.sortAddresses();
|
||||
}
|
||||
|
||||
get addresses() {
|
||||
return this._addresses;
|
||||
}
|
||||
|
||||
set addresses(value) {
|
||||
this._addresses = value;
|
||||
this.sortAddresses();
|
||||
}
|
||||
|
||||
setDefault(address) {
|
||||
let query = `/client/api/Clients/${this.$stateParams.id}`;
|
||||
let params = {defaultAddressFk: address.id};
|
||||
this.$http.patch(query, params).then(res => {
|
||||
if (res.data) {
|
||||
this.client.defaultAddressFk = res.data.defaultAddressFk;
|
||||
this.sortAddresses();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isDefaultAddress(address) {
|
||||
if (!this.client) return;
|
||||
return this.client.defaultAddressFk === address.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort address by default address
|
||||
*/
|
||||
sortAddresses() {
|
||||
if (!this.client || !this.addresses) return;
|
||||
this.$scope.model.data = this.addresses.sort((a, b) => {
|
||||
return this.isDefaultAddress(b) - this.isDefaultAddress(a);
|
||||
});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$http', '$scope', '$stateParams'];
|
||||
|
||||
ngModule.component('vnClientAddressIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,36 +1,69 @@
|
|||
import './index';
|
||||
import crudModel from 'core/mocks/crud-model';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientAddressIndex', () => {
|
||||
let controller;
|
||||
let $scope;
|
||||
let $state;
|
||||
let $stateParams;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('client'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$state_, _$httpBackend_) => {
|
||||
$state = _$state_;
|
||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$stateParams_, _$httpBackend_) => {
|
||||
$stateParams = _$stateParams_;
|
||||
$stateParams.id = 1;
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
$scope.model = {
|
||||
refresh: () => {}
|
||||
};
|
||||
controller = $componentController('vnClientAddressIndex', {$state, $scope});
|
||||
controller = $componentController('vnClientAddressIndex', {$stateParams, $scope});
|
||||
controller.client = {id: 101, defaultAddressFk: 121};
|
||||
controller.$scope.model = crudModel;
|
||||
}));
|
||||
|
||||
describe('setDefault()', () => {
|
||||
it('should perform a PATCH if the address is active and call the refresh method', () => {
|
||||
spyOn($scope.model, 'refresh');
|
||||
it('should perform a PATCH and set a value to defaultAddressFk property', () => {
|
||||
spyOn(controller, 'sortAddresses');
|
||||
let address = {id: 1};
|
||||
let data = {defaultAddressFk: address.id};
|
||||
let expectedResult = {defaultAddressFk: address.id};
|
||||
|
||||
let address = {id: 1, isActive: true};
|
||||
|
||||
$httpBackend.when('PATCH', `/client/api/Addresses/1`).respond(200);
|
||||
$httpBackend.expect('PATCH', `/client/api/Addresses/1`);
|
||||
$httpBackend.when('PATCH', `/client/api/Clients/1`, data).respond(200, expectedResult);
|
||||
$httpBackend.expect('PATCH', `/client/api/Clients/1`, data);
|
||||
controller.setDefault(address);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($scope.model.refresh).toHaveBeenCalledWith();
|
||||
expect(controller.client.defaultAddressFk).toEqual(1);
|
||||
expect(controller.sortAddresses).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDefaultAddress()', () => {
|
||||
it('should return true if a passed address is the current default one', () => {
|
||||
let address = {id: 121};
|
||||
let result = controller.isDefaultAddress(address);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if a passed address is the current default one', () => {
|
||||
let address = {id: 1};
|
||||
let result = controller.isDefaultAddress(address);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortAddresses()', () => {
|
||||
it('should return an array of addresses sorted by client defaultAddressFk', () => {
|
||||
controller.client.defaultAddressFk = 123;
|
||||
controller.addresses = [
|
||||
{id: 121, nickname: 'My address one'},
|
||||
{id: 122, nickname: 'My address two'},
|
||||
{id: 123, nickname: 'My address three'}];
|
||||
|
||||
controller.sortAddresses();
|
||||
|
||||
expect(controller.addresses[0].id).toEqual(123);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,16 +33,10 @@ export default class Controller {
|
|||
|
||||
this.$scope.watcher.submit().then(() => {
|
||||
if (shouldNotify)
|
||||
this.notifyChanges();
|
||||
this.vnApp.showMessage(this.$translate.instant('Notification sent!'));
|
||||
});
|
||||
}
|
||||
|
||||
notifyChanges() {
|
||||
this.$http.post(`/api/email/payment-update`, {clientFk: this.client.id}).then(
|
||||
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
|
||||
);
|
||||
}
|
||||
|
||||
hasPaymethodChanges() {
|
||||
let orgData = this.$scope.watcher.orgData;
|
||||
|
||||
|
|
|
@ -21,26 +21,6 @@ describe('Client', () => {
|
|||
$scope.watcher.orgData = {id: 101, name: 'Client name', payMethodFk: 4};
|
||||
}));
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should call notifyChanges() if there are changes on payMethod data`, () => {
|
||||
spyOn(controller, 'notifyChanges');
|
||||
controller.client.payMethodFk = 5;
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.notifyChanges).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('notifyChanges()', () => {
|
||||
it(`should perform a GET query`, () => {
|
||||
let params = {clientFk: 101};
|
||||
$httpBackend.when('POST', `/api/email/payment-update`, params).respond(true);
|
||||
$httpBackend.expect('POST', `/api/email/payment-update`, params);
|
||||
controller.notifyChanges();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasPaymethodChanges()', () => {
|
||||
it(`should return true if there are changes on payMethod data`, () => {
|
||||
controller.client.payMethodFk = 5;
|
||||
|
|
|
@ -102,7 +102,10 @@
|
|||
"url": "/create",
|
||||
"state": "client.card.address.create",
|
||||
"component": "vn-client-address-create",
|
||||
"description": "New address"
|
||||
"description": "New address",
|
||||
"params": {
|
||||
"client": "$ctrl.client"
|
||||
}
|
||||
}, {
|
||||
"url": "/:addressId/edit",
|
||||
"state": "client.card.address.edit",
|
||||
|
|
|
@ -124,13 +124,13 @@
|
|||
<vn-one>
|
||||
<h4 translate>Default address</h4>
|
||||
<vn-label-value label="Name"
|
||||
value="{{$ctrl.summary.addresses[0].nickname}}">
|
||||
value="{{$ctrl.summary.defaultAddress.nickname}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Street" ellipsize="false"
|
||||
value="{{$ctrl.summary.addresses[0].street}}">
|
||||
value="{{$ctrl.summary.defaultAddress.street}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="City"
|
||||
value="{{$ctrl.summary.addresses[0].city}}">
|
||||
value="{{$ctrl.summary.defaultAddress.city}}">
|
||||
</vn-label-value>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
|
|
|
@ -23,6 +23,11 @@ module.exports = Self => {
|
|||
type: 'String',
|
||||
description: `If it's and integer searchs by id, otherwise it searchs by name`,
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
arg: 'id',
|
||||
type: 'Integer',
|
||||
description: 'Item id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
arg: 'categoryFk',
|
||||
type: 'Integer',
|
||||
|
@ -33,6 +38,21 @@ module.exports = Self => {
|
|||
type: 'Integer',
|
||||
description: 'Type id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
arg: 'hasVisible',
|
||||
type: 'Boolean',
|
||||
description: 'Whether the the item has o not visible',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
arg: 'isActive',
|
||||
type: 'Boolean',
|
||||
description: 'Whether the the item is o not active',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
arg: 'salesPersonFk',
|
||||
type: 'Integer',
|
||||
description: 'The buyer of the item',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
|
@ -45,7 +65,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.filter = async(ctx, filter, tags) => {
|
||||
Self.filter = async(ctx, filter) => {
|
||||
let conn = Self.dataSource.connector;
|
||||
|
||||
let where = buildFilter(ctx.args, (param, value) => {
|
||||
|
@ -60,47 +80,82 @@ module.exports = Self => {
|
|||
return {'i.description': {like: `%${value}%`}};
|
||||
case 'categoryFk':
|
||||
return {'ic.id': value};
|
||||
case 'salesPersonFk':
|
||||
return {'t.workerFk': value};
|
||||
case 'typeFk':
|
||||
return {'t.id': value};
|
||||
return {'i.typeFk': value};
|
||||
case 'isActive':
|
||||
return {'i.isActive': value};
|
||||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(ctx.args.filter, {where});
|
||||
|
||||
let stmt = new ParameterizedSQL(
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
if (ctx.args.hasVisible === true)
|
||||
stmts.push('CALL cache.visible_refresh(@visibleCalc, true, 1)');
|
||||
|
||||
stmt = new ParameterizedSQL(
|
||||
`SELECT i.id, i.image, i.name, i.description,
|
||||
i.size, i.tag5, i.value5, i.tag6, i.value6,
|
||||
i.tag7, i.value7, i.tag8, i.value8, i.isActive,
|
||||
i.tag7, i.value7, i.tag8, i.value8,
|
||||
i.tag9, i.value9, i.tag10, i.value10, i.isActive,
|
||||
t.name type, u.nickname userNickname,
|
||||
t.name type, u.id userId,
|
||||
intr.description AS intrastat, i.stems,
|
||||
ori.code AS origin, t.name AS type,
|
||||
ic.name AS category, i.density, tc.description AS taxClass
|
||||
ic.name AS category, i.density, tc.description AS taxClass,
|
||||
b.grouping, b.packing, itn.code AS niche
|
||||
FROM item i
|
||||
JOIN itemType t ON t.id = i.typeFk
|
||||
LEFT JOIN itemType t ON t.id = i.typeFk
|
||||
LEFT JOIN itemCategory ic ON ic.id = t.categoryFk
|
||||
JOIN worker w ON w.id = t.workerFk
|
||||
JOIN account.user u ON u.id = w.userFk
|
||||
LEFT JOIN worker w ON w.id = t.workerFk
|
||||
LEFT JOIN account.user u ON u.id = w.userFk
|
||||
LEFT JOIN intrastat intr ON intr.id = i.intrastatFk
|
||||
LEFT JOIN producer pr ON pr.id = i.producerFk
|
||||
LEFT JOIN origin ori ON ori.id = i.originFk
|
||||
LEFT JOIN taxClass tc ON tc.id = i.taxClassFk`
|
||||
LEFT JOIN taxClass tc ON tc.id = i.taxClassFk
|
||||
LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = t.warehouseFk
|
||||
LEFT JOIN vn.buy b ON b.id = lb.buy_id
|
||||
LEFT JOIN itemPlacement itn ON itn.itemFk = i.id AND itn.warehouseFk = t.warehouseFk`
|
||||
);
|
||||
|
||||
if (ctx.args.hasVisible === true) {
|
||||
let joinAvailable = new ParameterizedSQL(
|
||||
`JOIN cache.visible v
|
||||
ON v.item_id = i.id AND v.calc_id = @visibleCalc`
|
||||
);
|
||||
stmt.merge(joinAvailable);
|
||||
}
|
||||
|
||||
if (ctx.args.tags) {
|
||||
let i = 1;
|
||||
for (let tag of ctx.args.tags) {
|
||||
if (tag.value == null) continue;
|
||||
let tAlias = `it${i++}`;
|
||||
for (const tag of ctx.args.tags) {
|
||||
const tAlias = `it${i++}`;
|
||||
|
||||
if (tag.tagFk) {
|
||||
stmt.merge({
|
||||
sql: `JOIN itemTag ${tAlias} ON ${tAlias}.itemFk = i.id
|
||||
sql: `JOIN vn.itemTag ${tAlias} ON ${tAlias}.itemFk = i.id
|
||||
AND ${tAlias}.tagFk = ?
|
||||
AND ${tAlias}.value = ?`,
|
||||
params: [tag.tagFk, tag.value]
|
||||
AND ${tAlias}.value LIKE ?`,
|
||||
params: [tag.tagFk, `%${tag.value}%`],
|
||||
});
|
||||
} else {
|
||||
stmt.merge({
|
||||
sql: `JOIN vn.itemTag ${tAlias} ON ${tAlias}.itemFk = i.id
|
||||
AND ${tAlias}.value LIKE ?`,
|
||||
params: [`%${tag.value}%`],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
return Self.rawStmt(stmt);
|
||||
let itemsIndex = stmts.push(stmt) - 1;
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let result = await conn.executeStmt(sql);
|
||||
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,8 +3,10 @@ const app = require('vn-loopback/server/server');
|
|||
describe('item new()', () => {
|
||||
let item;
|
||||
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.Item.destroyById(item.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create a new item, adding the name as a tag', async() => {
|
||||
|
|
|
@ -6,8 +6,10 @@ describe('regularize()', () => {
|
|||
const trashAddress = 11;
|
||||
let trashTicket;
|
||||
|
||||
afterAll(async () => {
|
||||
afterAll(async done => {
|
||||
await app.models.Ticket.destroyById(trashTicket.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create a new ticket and add a line', async() => {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('item updateTaxes()', () => {
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
let taxesInFixtures = [{id: 3, taxClassFk: 1}];
|
||||
|
||||
await app.models.Item.updateTaxes(taxesInFixtures);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should throw an error if the taxClassFk is blank', async() => {
|
||||
|
|
|
@ -57,6 +57,13 @@
|
|||
value="{{tag.value}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<vn-icon
|
||||
vn-tooltip="Item inactive"
|
||||
icon="icon-unavailable"
|
||||
ng-class="{bright: $ctrl.item.isActive == false}">
|
||||
</vn-icon>
|
||||
</div>
|
||||
<div class="quicklinks">
|
||||
<a ng-if="$ctrl.quicklinks.btnOne"
|
||||
vn-tooltip="{{::$ctrl.quicklinks.btnOne.tooltip}}"
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Regularize stock: Regularizar stock
|
||||
Type the visible quantity: Introduce la cantidad visible
|
||||
Item inactive: Artículo inactivo
|
|
@ -3,10 +3,39 @@
|
|||
<vn-auto>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !fetchedTag.value}"
|
||||
ng-repeat="fetchedTag in $ctrl.tags track by $index"
|
||||
title="{{::fetchedTag.name}}: {{::fetchedTag.value}}">
|
||||
{{::fetchedTag.value}}
|
||||
ng-class="::{empty: !$ctrl.item.value5}"
|
||||
title="{{::$ctrl.item.tag5}}: {{::$ctrl.item.value5}}">
|
||||
{{::$ctrl.item.value5}}
|
||||
</section>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !$ctrl.item.value6}"
|
||||
title="{{::$ctrl.item.tag6}}: {{::$ctrl.item.value6}}">
|
||||
{{::$ctrl.item.value6}}
|
||||
</section>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !$ctrl.item.value7}"
|
||||
title="{{::$ctrl.item.tag7}}: {{::$ctrl.item.value7}}">
|
||||
{{::$ctrl.item.value7}}
|
||||
</section>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !$ctrl.item.value8}"
|
||||
title="{{::$ctrl.item.tag8}}: {{::$ctrl.item.value8}}">
|
||||
{{::$ctrl.item.value8}}
|
||||
</section>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !$ctrl.item.value9}"
|
||||
title="{{::$ctrl.item.tag9}}: {{::$ctrl.item.value9}}">
|
||||
{{::$ctrl.item.value9}}
|
||||
</section>
|
||||
<section
|
||||
class="inline-tag ellipsize"
|
||||
ng-class="::{empty: !$ctrl.item.value10}"
|
||||
title="{{::$ctrl.item.tag10}}: {{::$ctrl.item.value10}}">
|
||||
{{::$ctrl.item.value10}}
|
||||
</section>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
|
@ -1,52 +1,8 @@
|
|||
import ngModule from '../module';
|
||||
import './style.scss';
|
||||
|
||||
export default class FetchedTags {
|
||||
set item(value) {
|
||||
if (value) {
|
||||
let tags = [];
|
||||
for (let i = 5; i < 5 + this.maxLength; i++) {
|
||||
if (value['tag' + i]) {
|
||||
let tagValue = value['value' + i];
|
||||
let tagKey = value['tag' + i];
|
||||
tags.push({name: tagKey, value: tagValue, position: i - 5});
|
||||
}
|
||||
}
|
||||
this.tags = tags;
|
||||
}
|
||||
this._item = value;
|
||||
}
|
||||
|
||||
get item() {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
set tags(value) {
|
||||
if (value) {
|
||||
let organizedTags = new Array(parseInt(this.maxLength));
|
||||
for (let i = 0; i < this.maxLength; i++) {
|
||||
let organizedTag = {};
|
||||
for (let j = 0; j < value.length; j++) {
|
||||
if (value[j].position === i) {
|
||||
organizedTag.name = value[j].name;
|
||||
organizedTag.value = value[j].value;
|
||||
}
|
||||
organizedTags[i] = JSON.parse(JSON.stringify(organizedTag));
|
||||
}
|
||||
}
|
||||
this._tags = organizedTags;
|
||||
}
|
||||
}
|
||||
|
||||
get tags() {
|
||||
return this._tags;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngModule.component('vnFetchedTags', {
|
||||
template: require('./index.html'),
|
||||
controller: FetchedTags,
|
||||
bindings: {
|
||||
maxLength: '<',
|
||||
item: '<',
|
||||
|
|
|
@ -22,13 +22,17 @@
|
|||
<vn-tr>
|
||||
<vn-th th-id="picture"></vn-th>
|
||||
<vn-th field="id" number>Id</vn-th>
|
||||
<vn-th th-id="grouping" number>Grouping</vn-th>
|
||||
<vn-th th-id="packing" number>Packing</vn-th>
|
||||
<vn-th th-id="description" style="text-align: center">Description</vn-th>
|
||||
<vn-th th-id="stems" number>Stems</vn-th>
|
||||
<vn-th th-id="size"number>Size</vn-th>
|
||||
<vn-th th-id="niche"number>Niche</vn-th>
|
||||
<vn-th th-id="type">Type</vn-th>
|
||||
<vn-th th-id="category">Category</vn-th>
|
||||
<vn-th th-id="intrastat">Intrastat</vn-th>
|
||||
<vn-th th-id="origin">Origin</vn-th>
|
||||
<vn-th th-id="salesperson">Sales person</vn-th>
|
||||
<vn-th th-id="salesperson">Buyer</vn-th>
|
||||
<vn-th th-id="density" number>Density</vn-th>
|
||||
<vn-th th-id="taxClass">Tax class</vn-th>
|
||||
<vn-th th-id="active" shrink>Active</vn-th>
|
||||
|
@ -53,6 +57,8 @@
|
|||
{{::item.id | zeroFill:6}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td number>{{::item.grouping | dashIfEmpty}}</vn-td>
|
||||
<vn-td number>{{::item.packing | dashIfEmpty}}</vn-td>
|
||||
<vn-td expand>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
|
@ -61,6 +67,8 @@
|
|||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td number>{{::item.stems}}</vn-td>
|
||||
<vn-td number>{{::item.size}}</vn-td>
|
||||
<vn-td number>{{::item.niche}}</vn-td>
|
||||
<vn-td>{{::item.type}}</vn-td>
|
||||
<vn-td>{{::item.category}}</vn-td>
|
||||
<vn-td>{{::item.intrastat}}</vn-td>
|
||||
|
|
|
@ -2,7 +2,7 @@ import ngModule from '../module';
|
|||
import './style.scss';
|
||||
|
||||
class Controller {
|
||||
constructor($http, $state, $scope) {
|
||||
constructor($http, $state, $scope, $stateParams) {
|
||||
this.$http = $http;
|
||||
this.$state = $state;
|
||||
this.$ = $scope;
|
||||
|
@ -13,6 +13,14 @@ class Controller {
|
|||
id: false,
|
||||
actions: false
|
||||
};
|
||||
|
||||
if (!$stateParams.q)
|
||||
this.filter = {hasVisible: true, isActive: true};
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
if (this.filter)
|
||||
this.onSearch(this.filter);
|
||||
}
|
||||
|
||||
stopEvent(event) {
|
||||
|
@ -83,7 +91,7 @@ class Controller {
|
|||
this.$.preview.show();
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$http', '$state', '$scope'];
|
||||
Controller.$inject = ['$http', '$state', '$scope', '$stateParams'];
|
||||
|
||||
ngModule.component('vnItemIndex', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -46,6 +46,30 @@
|
|||
label="Description"
|
||||
model="filter.description">
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
disabled="false"
|
||||
field="filter.salesPersonFk"
|
||||
url="/client/api/Clients/activeWorkersWithRole"
|
||||
show-field="nickname"
|
||||
search-function="{firstName: $search}"
|
||||
value-field="id"
|
||||
where="{role: 'employee'}"
|
||||
label="Buyer">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
vn-one
|
||||
label="With visible"
|
||||
field="filter.hasVisible">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
vn-one
|
||||
label="Active"
|
||||
field="filter.isActive"
|
||||
triple-state="true">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-repeat="itemTag in filter.tags">
|
||||
<vn-autocomplete
|
||||
|
|
|
@ -2,8 +2,10 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('order addToOrder()', () => {
|
||||
let rowToDelete;
|
||||
afterAll(async() => {
|
||||
afterAll(async done => {
|
||||
await app.models.OrderRow.removes({rows: [rowToDelete], actualOrderId: 20});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should add a row to a given order', async() => {
|
||||
|
|
|
@ -4,10 +4,12 @@ describe('order removes()', () => {
|
|||
let row;
|
||||
let newRow;
|
||||
|
||||
beforeAll(async() => {
|
||||
beforeAll(async done => {
|
||||
row = await app.models.OrderRow.findOne({where: {id: 12}});
|
||||
row.id = null;
|
||||
newRow = await app.models.OrderRow.create(row);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should throw an error if rows property is empty', async() => {
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
|
||||
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.remoteMethod('filter', {
|
||||
description: 'Find all instances of the model matched by filter from the data source.',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
}, {
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
|
||||
}, {
|
||||
arg: 'search',
|
||||
type: 'String',
|
||||
description: `If it's and integer searchs by id, otherwise it searchs by nickname`
|
||||
}, {
|
||||
arg: 'from',
|
||||
type: 'Date',
|
||||
description: `The from date`
|
||||
}, {
|
||||
arg: 'to',
|
||||
type: 'Date',
|
||||
description: `The to date`
|
||||
}, {
|
||||
arg: 'id',
|
||||
type: 'Integer',
|
||||
description: `The ticket id`
|
||||
}, {
|
||||
arg: 'clientFk',
|
||||
type: 'Integer',
|
||||
description: `The client id`
|
||||
}, {
|
||||
arg: 'ticketFk',
|
||||
type: 'Integer',
|
||||
description: `The ticket id`
|
||||
}, {
|
||||
arg: 'agencyModeFk',
|
||||
type: 'Integer',
|
||||
description: `The agency mode id`
|
||||
}, {
|
||||
arg: 'workerFk',
|
||||
type: 'Integer',
|
||||
description: `The salesperson id`
|
||||
}, {
|
||||
arg: 'isConfirmed',
|
||||
type: 'Boolean',
|
||||
description: `Order is confirmed`
|
||||
}
|
||||
],
|
||||
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 {'o.id': value};
|
||||
case 'from':
|
||||
return {'o.date_send': {gte: value}};
|
||||
case 'to':
|
||||
return {'o.date_send': {lte: value}};
|
||||
case 'workerFk':
|
||||
return {'c.salesPersonFk': value};
|
||||
case 'clientFk':
|
||||
return {'o.customer_id': value};
|
||||
case 'agencyModeFk':
|
||||
return {'o.agency_id': value};
|
||||
case 'sourceApp':
|
||||
return {'o.source_app': value};
|
||||
case 'ticketFk':
|
||||
return {'ort.ticketFk': value};
|
||||
case 'isConfirmed':
|
||||
return {'o.confirmed': value ? 1 : 0};
|
||||
case 'id':
|
||||
param = `o.${param}`;
|
||||
return {[param]: value};
|
||||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.filter');
|
||||
|
||||
stmt = new ParameterizedSQL(
|
||||
`CREATE TEMPORARY TABLE tmp.filter
|
||||
(INDEX (id))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
o.id,
|
||||
o.date_send landed,
|
||||
o.date_make created,
|
||||
o.customer_id clientFk,
|
||||
o.agency_id agencyModeFk,
|
||||
o.address_id addressFk,
|
||||
o.company_id companyFk,
|
||||
o.source_app sourceApp,
|
||||
o.confirmed isConfirmed,
|
||||
c.name clientName,
|
||||
u.nickname workerNickname,
|
||||
u.id userId,
|
||||
co.code companyCode
|
||||
FROM hedera.order o
|
||||
LEFT JOIN address a ON a.id = o.address_id
|
||||
LEFT JOIN agencyMode am ON am.id = o.agency_id
|
||||
LEFT JOIN client c ON c.id = o.customer_id
|
||||
LEFT JOIN worker wk ON wk.id = c.salesPersonFk
|
||||
LEFT JOIN account.user u ON u.id = wk.userFk
|
||||
LEFT JOIN company co ON co.id = o.company_id`);
|
||||
|
||||
if (ctx.args && ctx.args.ticketFk) {
|
||||
stmt.merge({
|
||||
sql: `LEFT JOIN orderTicket ort ON ort.orderFk = o.id`
|
||||
});
|
||||
}
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
stmts.push(stmt);
|
||||
|
||||
stmts.push(`
|
||||
CREATE TEMPORARY TABLE tmp.order
|
||||
(INDEX (orderFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT id AS orderFk
|
||||
FROM tmp.filter`);
|
||||
|
||||
stmts.push('CALL hedera.orderGetTotal()');
|
||||
|
||||
let orderIndex = stmts.push(`
|
||||
SELECT f.*, ot.*
|
||||
FROM tmp.filter f
|
||||
LEFT JOIN tmp.orderTotal ot ON ot.orderFk = f.id`) - 1;
|
||||
|
||||
stmts.push(`
|
||||
DROP TEMPORARY TABLE
|
||||
tmp.order,
|
||||
tmp.orderTotal`);
|
||||
|
||||
stmts.push(
|
||||
`DROP TEMPORARY TABLE
|
||||
tmp.filter`);
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let result = await conn.executeStmt(sql);
|
||||
|
||||
return result[orderIndex];
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue