This commit is contained in:
Juan Ferrer 2019-03-20 12:22:34 +01:00
commit e147f318cb
118 changed files with 1881 additions and 642 deletions

View File

@ -9,7 +9,7 @@ Salix is also the scientific name of a beautifull tree! :)
Required applications.
* Visual Studio Code
* Node.js = 8.15.0
* Node.js = 10.15.3 LTS
* Docker
In Visual Studio Code we use the ESLint extension. Open Visual Studio Code, press Ctrl+P and paste the following command.

View File

@ -1,7 +1,7 @@
[client]
enable_cleartext_plugin = ON
host = localhost
port = 3306
user = root
password = password
ssl-mode = DISABLED
enable_cleartext_plugin = ON

View File

@ -60,6 +60,7 @@ echo "[INFO] Config file: $INI_FILE"
echo "[INFO] Applying changes"
# Import changes
for file in $DIR/install/changes/*.sql; do
echo "[INFO] -> $file"
mysql --defaults-file="$INI_FILE" < $file

View File

@ -2,8 +2,6 @@
export MYSQL_PWD=root
# Dump structure
echo "[INFO] -> Imported ./dump/truncateAll.sql"
mysql -u root -f < ./dump/truncateAll.sql
echo "[INFO] -> Imported ./dump/structure.sql"
mysql -u root -f < ./dump/structure.sql
echo "[INFO] -> Imported ./dump/mysqlPlugins.sql"

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (149, 'Sip', '*', 'WRITE', 'ALLOW', 'ROLE', 'hr');
INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (150, 'Sip', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (151, 'Department','*','READ','ALLOW','ROLE','employee');
INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (152, 'Department','*','WRITE','ALLOW','ROLE','hr');

View File

@ -1,10 +1,7 @@
USE `hedera`;
DROP procedure IF EXISTS `orderGetTotal`;
DROP procedure IF EXISTS `hedera`.`orderGetTotal`;
DELIMITER $$
USE `hedera`$$
CREATE DEFINER=`root`@`%` PROCEDURE `orderGetTotal`()
CREATE DEFINER=`root`@`%` PROCEDURE `hedera`.`orderGetTotal`()
BEGIN
/**
* Calcula el total con IVA para un conjunto de orders.

View File

@ -0,0 +1,72 @@
USE `vn`;
DROP procedure IF EXISTS `ticketCalculateSale`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticketCalculateSale`(IN vSale BIGINT)
proc: BEGIN
/*
Este procedimiento bioniza una linea de movimiento
*/
DECLARE vShipped DATE;
DECLARE vWarehouse SMALLINT;
DECLARE vAgencyMode INT;
DECLARE vAddress INT;
DECLARE vTicket BIGINT;
DECLARE vItem BIGINT;
DECLARE vLanded DATE;
DECLARE vTicketFree BOOLEAN DEFAULT TRUE;
SELECT FALSE
INTO vTicketFree
FROM vn.ticket t
JOIN vn.sale s ON s.ticketFk = t.id
LEFT JOIN vn.ticketState ts ON ts.ticketFk = t.id
WHERE s.id = vSale
AND (t.refFk != "" OR (ts.alertLevel > 0 AND s.price != 0))
LIMIT 1;
SELECT ticketFk, itemFk
INTO vTicket, vItem
FROM sale
WHERE id = vSale;
SELECT t.warehouseFk, DATE(t.shipped), t.addressFk, t.agencyModeFk, t.landed
INTO vWarehouse, vShipped, vAddress, vAgencyMode, vLanded
FROM agencyMode a
JOIN ticket t ON t.agencyModeFk = a.id
WHERE t.id = vTicket;
DROP TEMPORARY TABLE IF EXISTS tmp.agencyHourGetShipped;
CREATE TEMPORARY TABLE tmp.agencyHourGetShipped ENGINE = MEMORY
SELECT vWarehouse warehouseFk, vShipped shipped, vLanded landed;
CALL buyUltimate (vWarehouse, vShipped); -- rellena la tabla tmp.buyUltimate con la ultima compra
DELETE FROM tmp.buyUltimate WHERE itemFk != vItem;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
CREATE TEMPORARY TABLE tmp.ticketLot
SELECT vWarehouse warehouseFk, NULL available, vItem itemFk, buyFk
FROM tmp.buyUltimate
WHERE itemFk = vItem;
CALL ticketComponentCalculate(vAddress, vAgencyMode);
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
CREATE TEMPORARY TABLE tmp.sale
(PRIMARY KEY (saleFk)) ENGINE = MEMORY
SELECT vSale saleFk,vWarehouse warehouseFk;
CALL ticketComponentUpdateSale(IF(vTicketFree,1,6)); -- si el ticket esta facturado, respeta los precios
-- Log
INSERT INTO vn.ticketLog (originFk, userFk, `action`, description)
VALUES (vTicket, account.userGetId(), 'update', CONCAT('Bionizo linea id ', vSale));
-- Limpieza
DROP TEMPORARY TABLE tmp.buyUltimate;
END$$
DELIMITER ;

View File

@ -0,0 +1,23 @@
/* Ejecutar en prod * /
/* USE `vn2008`;
ALTER TABLE vn2008.department ADD `depth` int DEFAULT 0 NOT NULL;
ALTER TABLE vn2008.department ADD sons int DEFAULT 0 NOT NULL;
USE `vn`;
CREATE
OR REPLACE
VIEW `vn`.`department` AS select
`b`.`department_id` AS `id`,
`b`.`name` AS `name`,
`b`.`father_id` AS `fatherFk`,
`b`.`production` AS `isProduction`,
`b`.`lft` AS `lft`,
`b`.`rgt` AS `rgt`,
`b`.`isSelected` AS `isSelected`,
`b`.`depth` AS `depth`,
`b`.`sons` AS `sons`
from
`vn2008`.`department` `b`; */

View File

@ -0,0 +1,165 @@
USE `vn`;
DROP procedure IF EXISTS `ticketGetProblems`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticketGetProblems`()
BEGIN
/**
* Obtiene los problemas de uno o varios tickets
*
* @table tmp.ticketGetProblems(ticketFk, clientFk, warehouseFk, shipped)
* @return tmp.ticketProblems
*/
DECLARE vWarehouse INT;
DECLARE vDate DATE;
DECLARE vAvailableCache INT;
DECLARE vVisibleCache INT;
DECLARE vDone INT DEFAULT 0;
DECLARE vCursor CURSOR FOR
SELECT DISTINCT tt.warehouseFk, date(tt.shipped)
FROM tmp.ticketGetProblems tt
WHERE DATE(tt.shipped) BETWEEN CURDATE()
AND TIMESTAMPADD(DAY, 1.9, CURDATE());
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketProblems;
CREATE TEMPORARY TABLE tmp.ticketProblems (
ticketFk INT(11) PRIMARY KEY,
isFreezed INTEGER(1) DEFAULT 0,
risk DECIMAL(10,2) DEFAULT 0,
hasTicketRequest INTEGER(1) DEFAULT 0,
isAvailable INTEGER(1) DEFAULT 1
) ENGINE = MEMORY;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketList;
CREATE TEMPORARY TABLE tmp.ticketList
(PRIMARY KEY (ticketFk))
ENGINE = MEMORY
SELECT tp.ticketFk, c.id clientFk
FROM tmp.ticketGetProblems tp
JOIN vn.client c ON c.id = tp.clientFk;
-- Inserta tickets de clientes congelados
INSERT INTO tmp.ticketProblems(ticketFk, isFreezed)
SELECT DISTINCT tl.ticketFk, 1
FROM tmp.ticketList tl
JOIN vn.client c ON c.id = tl.clientFk
WHERE c.isFreezed;
DELETE tl FROM tmp.ticketList tl
JOIN tmp.ticketProblems tp ON tl.ticketFk = tp.ticketFk;
DROP TEMPORARY TABLE IF EXISTS tmp.clientGetDebt;
CREATE TEMPORARY TABLE tmp.clientGetDebt
(PRIMARY KEY (clientFk))
ENGINE = MEMORY
SELECT DISTINCT clientFk
FROM tmp.ticketList;
CALL clientGetDebt(CURDATE());
-- Inserta tickets de clientes con riesgo
INSERT INTO tmp.ticketProblems(ticketFk, risk)
SELECT DISTINCT tl.ticketFk, r.risk
FROM tmp.ticketList tl
JOIN vn.ticket t ON t.id = tl.ticketFk
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
JOIN tmp.risk r ON r.clientFk = t.clientFk
JOIN vn.client c ON c.id = t.clientFk
WHERE r.risk > c.credit + 10
AND a.deliveryMethodFk != 3
ON DUPLICATE KEY UPDATE
risk = r.risk;
DELETE tl FROM tmp.ticketList tl
JOIN tmp.ticketProblems tp ON tl.ticketFk = tp.ticketFk;
-- Inserta tickets que tengan codigos 100
INSERT INTO tmp.ticketProblems(ticketFk, hasTicketRequest)
SELECT DISTINCT tl.ticketFk, 1
FROM tmp.ticketList tl
JOIN vn.ticketRequest tr ON tr.ticketFk = tl.ticketFk
WHERE tr.isOK IS NULL
ON DUPLICATE KEY UPDATE
hasTicketRequest = 1;
DELETE tl FROM tmp.ticketList tl
JOIN tmp.ticketProblems tp ON tl.ticketFk = tp.ticketFk;
OPEN vCursor;
WHILE NOT vDone
DO
FETCH vCursor INTO vWarehouse, vDate;
CALL cache.visible_refresh(vVisibleCache, FALSE, vWarehouse);
CALL cache.available_refresh(vAvailableCache, FALSE, vWarehouse, vDate);
-- Inserta tickets con articulos que no tegan disponible
INSERT INTO tmp.ticketProblems(ticketFk, isAvailable)
SELECT tl.ticketFk, 0
FROM tmp.ticketList tl
JOIN vn.ticket t ON t.id = tl.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.itemType it on it.id = i.typeFk
LEFT JOIN cache.visible v ON i.id = v.item_id
AND v.calc_id = vVisibleCache
LEFT JOIN cache.available av ON av.item_id = i.id
AND av.calc_id = vAvailableCache
WHERE date(t.shipped) = vDate
AND categoryFk != 6
AND s.quantity > IFNULL(v.visible, 0)
AND IFNULL(av.available, 0) < 0
AND s.isPicked = FALSE
AND NOT i.generic
AND vWarehouse = t.warehouseFk
GROUP BY tl.ticketFk
ON DUPLICATE KEY UPDATE
isAvailable = 0;
DELETE tl FROM tmp.ticketList tl
JOIN tmp.ticketProblems tp ON tl.ticketFk = tp.ticketFk;
INSERT INTO tmp.ticketProblems(ticketFk, isAvailable)
SELECT tl.ticketFk, 0
FROM tmp.ticketList tl
JOIN vn.ticket t ON t.id = tl.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.itemType it on it.id = i.typeFk
LEFT JOIN cache.visible v ON i.id = v.item_id AND v.calc_id = vVisibleCache
LEFT JOIN cache.available av ON av.item_id = i.id AND av.calc_id = vAvailableCache
WHERE IFNULL(av.available, 0) >= 0
AND s.quantity > IFNULL(v.visible, 0)
AND s.isPicked = FALSE
AND s.reserved = FALSE
AND it.categoryFk != 6
AND date(t.shipped) = vDate
AND NOT i.generic
AND CURDATE() = vDate
AND t.warehouseFk = vWarehouse
GROUP BY tl.ticketFk
ON DUPLICATE KEY UPDATE
isAvailable = 0;
DELETE tl FROM tmp.ticketList tl
JOIN tmp.ticketProblems tp ON tl.ticketFk = tp.ticketFk;
END WHILE;
CLOSE vCursor;
SELECT * FROM tmp.ticketProblems;
DROP TEMPORARY TABLE
tmp.clientGetDebt,
tmp.ticketList;
END$$
DELIMITER ;

View File

@ -0,0 +1,86 @@
/* Ejecutar en prod * /
/* DROP PROCEDURE IF EXISTS nst.NodeAdd;
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeAdd`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45), IN `vParentFk` INT, IN `vChild` VARCHAR(100))
BEGIN
DECLARE vSql TEXT;
DECLARE vTableClone VARCHAR(45);
SET vTableClone = CONCAT(vTable, 'Clone');
CALL util.exec(CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp.', vTableClone));
CALL util.exec(CONCAT(
'CREATE TEMPORARY TABLE tmp.', vTableClone,
' ENGINE = MEMORY'
' SELECT * FROM ', vScheme, '.', vTable
));
-- Check parent childs
SET vSql = sql_printf('
SELECT COUNT(c.id) INTO @childs
FROM %t.%t p
LEFT JOIN %t.%t c ON c.lft BETWEEN p.lft AND p.rgt AND c.id != %v
WHERE p.id = %v',
vScheme, vTable, 'tmp', vTableClone, vParentFk, vParentFk);
SET @qrySql := vSql;
PREPARE stmt FROM @qrySql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Select left from last child
IF @childs = 0 THEN
SET vSql = sql_printf('SELECT lft INTO @vLeft FROM %t.%t WHERE id = %v', vScheme, vTable, vParentFk);
SET @qrySql := vSql;
ELSE
SET vSql = sql_printf('
SELECT c.rgt INTO @vLeft
FROM %t.%t p
JOIN %t.%t c ON c.lft BETWEEN p.lft AND p.rgt
WHERE p.id = %v
ORDER BY c.lft
DESC LIMIT 1',
vScheme, vTable, 'tmp', vTableClone, vParentFk);
SET @qrySql := vSql;
END IF;
PREPARE stmt FROM @qrySql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Update right
SET vSql = sql_printf('UPDATE %t.%t SET rgt = rgt + 2 WHERE rgt > %v ORDER BY rgt DESC', vScheme, vTable, @vLeft);
SET @qrySql := vSql;
PREPARE stmt FROM @qrySql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET vSql = sql_printf('UPDATE %t.%t SET lft = lft + 2 WHERE lft > %v ORDER BY lft DESC', vScheme, vTable, @vLeft);
SET @qrySql := vSql;
PREPARE stmt FROM @qrySql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Escape character
SET vChild = REPLACE(vChild, "'", "\\'");
-- Add child
SET vSql = sql_printf('INSERT INTO %t.%t (name, lft, rgt) VALUES (%v, %v, %v)', vScheme, vTable, vChild, @vLeft + 1, @vLeft + 2);
SET @qrySql := vSql;
PREPARE stmt FROM @qrySql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT id, name, lft, rgt, depth, sons FROM vn.department
WHERE id = LAST_INSERT_ID();
CALL util.exec(CONCAT('DROP TEMPORARY TABLE tmp.', vTableClone));
END$$
DELIMITER ;
*/

View File

@ -0,0 +1,21 @@
DROP procedure IF EXISTS `hedera`.`basketGetTax`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `hedera`.`basketGetTax`()
READS SQL DATA
BEGIN
/**
* Returns the taxes for the current client basket.
*
* @treturn tmp.orderTax
*/
DROP TEMPORARY TABLE IF EXISTS tmp.order;
CREATE TEMPORARY TABLE tmp.order
ENGINE = MEMORY
SELECT myBasketGetId() orderFk;
CALL orderGetTax();
DROP TEMPORARY TABLE IF EXISTS tmp.order;
END$$
DELIMITER ;

View File

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

View File

@ -0,0 +1 @@
ALTER TABLE vn.itemLog MODIFY COLUMN userFk int(10) unsigned NULL;

View File

@ -0,0 +1,4 @@
USE `vn`;
CREATE UNIQUE INDEX zoneGeo_lft_IDX USING BTREE ON vn.zoneGeo (lft);
CREATE UNIQUE INDEX zoneGeo_rgt_IDX USING BTREE ON vn.zoneGeo (rgt);

View File

@ -0,0 +1,16 @@
ALTER TABLE `vn`.`itemLog`
CHANGE COLUMN `id` `id` INT(11) NOT NULL AUTO_INCREMENT ,
ADD PRIMARY KEY (`id`);
ALTER TABLE `vn`.`itemLog`
DROP FOREIGN KEY `itemLogUserFk`;
ALTER TABLE `vn`.`itemLog`
CHANGE COLUMN `id` `id` INT(11) NOT NULL AUTO_INCREMENT ,
CHANGE COLUMN `userFk` `userFk` INT(10) UNSIGNED NULL DEFAULT NULL ;
ALTER TABLE `vn`.`itemLog`
ADD CONSTRAINT `itemLogUserFk`
FOREIGN KEY (`userFk`)
REFERENCES `account`.`user` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;

View File

@ -1,3 +1,3 @@
/* Añadir a producción cuando se suba salix */
DROP TRIGGER vn2008.ClientesAfterInsert;
DROP TRIGGER IF EXISTS vn2008.ClientesAfterInsert;

View File

@ -12,7 +12,8 @@ BEGIN
UPDATE Consignatarios SET predeterminada = FALSE
WHERE Id_cliente = NEW.Id_cliente;
UPDATE Consignatarios SET predeterminada = TRUE
WHERE Id_consigna = NEW.default_address;
END IF;
END$$
DELIMITER ;

View File

@ -204,6 +204,11 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
(200, 'Missing', NULL, 'Missing man', 'Anton', 'The space', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 4, NULL, 1, 0, 1, 0, NULL, 1, 0, NULL, 0, 1),
(400, 'Trash', NULL, 'Garbage man', 'Unknown name', 'New York city', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 4, NULL, 1, 0, 1, 0, NULL, 1, 0, NULL, 0, 1);
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `phone`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'SILLA', 46460, 623111111, 1, CONCAT(name,'@verdnatura.es'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, CURDATE(), 1
FROM `account`.`role` `r`
WHERE `r`.`hasLogin` = 1;
INSERT INTO `vn`.`clientManaCache`(`clientFk`, `mana`, `dated`)
VALUES
( 101, 50, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)),
@ -258,6 +263,11 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
(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);
INSERT INTO `vn`.`address`( `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `isActive`, `clientFk`, `agencyModeFk`, `isDefaultAddress`)
SELECT name, CONCAT(name, 'Street'), 'SILLA', 46460, 1, 1, id, 2, 1
FROM `account`.`role` `r`
WHERE `r`.`hasLogin` = 1;
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;
@ -271,6 +281,11 @@ 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;
UPDATE `vn`.`client` `c`
JOIN `vn`.`address` `a` ON `a`.`clientFk` = `c`.`id`
SET `c`.`defaultAddressFk` = `a`.`id`
WHERE `defaultAddressFk` IS NULL;
INSERT INTO `vn`.`clientCredit`(`id`, `clientFk`, `workerFk`, `amount`, `created`)
VALUES
(1 , 101, 5, 300, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)),

View File

@ -1,37 +0,0 @@
DROP PROCEDURE IF EXISTS mysql.truncateAll;
DELIMITER $$
CREATE PROCEDURE mysql.truncateAll()
BEGIN
DECLARE vSchema VARCHAR(255);
DECLARE vTable VARCHAR(255);
DECLARE vDone BOOL;
DECLARE cTables CURSOR FOR
SELECT `TABLE_SCHEMA`, `TABLE_NAME`
FROM `information_schema`.`TABLES`
WHERE `TABLE_TYPE` = 'BASE TABLE'
AND `TABLE_SCHEMA` NOT IN ('information_schema', 'mysql', 'performance_schema');
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
SET FOREIGN_KEY_CHECKS = FALSE;
OPEN cTables;
l: LOOP
SET vDone = FALSE;
FETCH cTables INTO vSchema, vTable;
IF vDone THEN
LEAVE l;
END IF;
SET @stmt = CONCAT('TRUNCATE TABLE `', vSchema, '`.`', vTable, '`');
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cTables;
SET FOREIGN_KEY_CHECKS = TRUE;
END$$
DELIMITER ;

View File

@ -87,6 +87,7 @@ export default {
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input',
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
newBankEntityCode: 'vn-client-billing-data > vn-dialog vn-textfield[label="Code"] input',
acceptBankEntityButton: 'vn-client-billing-data > vn-dialog button[response="ACCEPT"]',
saveButton: `${components.vnSubmit}`
},
@ -171,7 +172,8 @@ export default {
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"]`,
searchIcon: `vn-item-index vn-searchbar vn-icon[icon="search"]`,
goBackToModuleIndexButton: `vn-item-descriptor a[href="#!/item/index"]`,
createItemButton: `${components.vnFloatButton}`,
searchResult: `vn-item-index a.vn-tr`,
searchResultPreviewButton: `vn-item-index .buttons > [icon="desktop_windows"]`,
@ -179,7 +181,23 @@ export default {
acceptClonationAlertButton: `vn-item-index [vn-id="clone"] [response="ACCEPT"]`,
searchItemInput: `vn-searchbar vn-textfield input`,
searchButton: `vn-searchbar vn-icon[icon="search"]`,
closeItemSummaryPreview: 'vn-item-index [vn-id="preview"] button.close'
closeItemSummaryPreview: 'vn-item-index [vn-id="preview"] button.close',
fieldsToShowButton: 'vn-item-index vn-table > div.ng-scope > div > vn-icon-button[icon="menu"]',
fieldsToShowForm: 'vn-item-index > div > vn-card > div > vn-table > div.ng-scope > div > vn-dialog > div > form',
firstItemImage: 'vn-item-index vn-tbody > a:nth-child(1) > vn-td:nth-child(1)',
firstItemId: 'vn-item-index vn-tbody > a:nth-child(1) > vn-td:nth-child(2)',
idCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(2) > vn-check > md-checkbox',
stemsCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(3) > vn-check > md-checkbox',
sizeCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(4) > vn-check > md-checkbox',
nicheCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(5) > vn-check > md-checkbox',
typeCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(6) > vn-check > md-checkbox',
categoryCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(7) > vn-check > md-checkbox',
intrastadCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(8) > vn-check > md-checkbox',
originCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(9) > vn-check > md-checkbox',
buyerCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(10) > vn-check > md-checkbox',
destinyCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(11) > vn-check > md-checkbox',
taxClassCheckbox: 'vn-item-index vn-dialog form vn-horizontal:nth-child(12) > vn-check > md-checkbox',
saveFieldsButton: 'vn-item-index vn-dialog vn-horizontal:nth-child(16) > vn-button > button'
},
itemCreateView: {
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
@ -195,7 +213,9 @@ export default {
regularizeQuantityInput: `vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input`,
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]',
editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]',
regularizeSaveButton: `vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button`
regularizeSaveButton: `vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button`,
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]',
navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]'
},
itemBasicData: {
basicDataButton: `vn-left-menu a[ui-sref="item.card.data"]`,
@ -325,7 +345,7 @@ export default {
packagesButton: `vn-left-menu a[ui-sref="ticket.card.package.index"]`,
firstPackageAutocomplete: `vn-autocomplete[label="Package"]`,
firstQuantityInput: `vn-textfield[label="Quantity"] input`,
firstRemovePackageButton: `vn-icon[vn-tooltip="Remove package"]`,
firstRemovePackageButton: `vn-icon-button[vn-tooltip="Remove package"]`,
addPackageButton: `vn-icon-button[vn-tooltip="Add package"]`,
clearPackageAutocompleteButton: `vn-autocomplete[label="Package"] > div > div > div > vn-icon > i`,
savePackagesButton: `${components.vnSubmit}`
@ -425,7 +445,7 @@ export default {
addServiceButton: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(3) > vn-icon-button > button > vn-icon',
firstDescriptionInput: 'vn-ticket-service vn-textfield[label="Description"] input',
firstQuantityInput: 'vn-ticket-service vn-textfield[label="Quantity"] input',
firstPriceInput: 'vn-ticket-service vn-textfield[label="Price"] input',
firstPriceInput: 'vn-ticket-service vn-input-number[label="Price"] input',
firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
fistDeleteServiceButton: 'vn-ticket-card > vn-main-block > div.content-block.ng-scope > vn-ticket-service > form > vn-card > div > vn-one:nth-child(1) > vn-horizontal:nth-child(1) > vn-auto > vn-icon-button[icon="delete"]',
serviceLine: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(2) > vn-horizontal',
@ -515,4 +535,9 @@ export default {
confirmOrder: 'vn-order-line > vn-vertical > vn-button-bar > vn-button > button',
confirmButton: 'vn-order-line > vn-confirm button[response="ACCEPT"]',
},
workerPbx: {
extensionInput: 'vn-worker-pbx vn-textfield[model="$ctrl.worker.sip.extension"] input',
passwordInput: 'vn-worker-pbx vn-textfield[model="$ctrl.worker.sip.secret"] input',
saveButton: 'vn-worker-pbx vn-submit[label="Save"] input'
}
};

View File

@ -44,6 +44,7 @@ describe('Client Edit pay method path', () => {
const newcode = await nightmare
.waitToClick(selectors.clientPayMethod.newBankEntityButton)
.write(selectors.clientPayMethod.newBankEntityName, 'Gotham City Bank')
.write(selectors.clientPayMethod.newBankEntityCode, 9999)
.write(selectors.clientPayMethod.newBankEntityBIC, 'GTHMCT')
.waitToClick(selectors.clientPayMethod.acceptBankEntityButton)
.waitToGetProperty(`${selectors.clientPayMethod.swiftBicAutocomplete} input`, 'value');

View File

@ -0,0 +1,88 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('Item index path', () => {
const nightmare = createNightmare();
beforeAll(() => {
nightmare
.loginAndModule('salesPerson', 'item')
.waitToClick(selectors.itemsIndex.searchIcon);
});
it('should click on the fields to show button to open the list of columns to show', async() => {
const visible = await nightmare
.waitToClick(selectors.itemsIndex.fieldsToShowButton)
.isVisible(selectors.itemsIndex.fieldsToShowForm);
expect(visible).toBeTruthy();
});
it('should unmark all checkboxes except the first and the last ones', async() => {
const result = await nightmare
.waitToClick(selectors.itemsIndex.idCheckbox)
.waitToClick(selectors.itemsIndex.stemsCheckbox)
.waitToClick(selectors.itemsIndex.sizeCheckbox)
.waitToClick(selectors.itemsIndex.nicheCheckbox)
.waitToClick(selectors.itemsIndex.typeCheckbox)
.waitToClick(selectors.itemsIndex.categoryCheckbox)
.waitToClick(selectors.itemsIndex.intrastadCheckbox)
.waitToClick(selectors.itemsIndex.originCheckbox)
.waitToClick(selectors.itemsIndex.buyerCheckbox)
.waitToClick(selectors.itemsIndex.destinyCheckbox)
// .waitToClick(selectors.itemsIndex.taxClassCheckbox)
.waitToClick(selectors.itemsIndex.saveFieldsButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
it('should navigate forth and back to see the images column is still visible', async() => {
const imageVisible = await nightmare
.waitToClick(selectors.itemsIndex.searchResult)
.waitToClick(selectors.itemsIndex.goBackToModuleIndexButton)
.waitToClick(selectors.itemsIndex.searchIcon)
.wait(selectors.itemsIndex.searchResult)
.isVisible(selectors.itemsIndex.firstItemImage);
expect(imageVisible).toBeTruthy();
});
it('should check the ids column is not visible', async() => {
const idVisible = await nightmare
.isVisible(selectors.itemsIndex.firstItemId);
expect(idVisible).toBeFalsy();
});
it('should mark all unchecked boxes to leave the index as it was', async() => {
const result = await nightmare
.waitToClick(selectors.itemsIndex.fieldsToShowButton)
.waitToClick(selectors.itemsIndex.idCheckbox)
.waitToClick(selectors.itemsIndex.stemsCheckbox)
.waitToClick(selectors.itemsIndex.sizeCheckbox)
.waitToClick(selectors.itemsIndex.nicheCheckbox)
.waitToClick(selectors.itemsIndex.typeCheckbox)
.waitToClick(selectors.itemsIndex.categoryCheckbox)
.waitToClick(selectors.itemsIndex.intrastadCheckbox)
.waitToClick(selectors.itemsIndex.originCheckbox)
.waitToClick(selectors.itemsIndex.buyerCheckbox)
.waitToClick(selectors.itemsIndex.destinyCheckbox)
// .waitToClick(selectors.itemsIndex.taxClassCheckbox)
.waitToClick(selectors.itemsIndex.saveFieldsButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
it('should now navigate forth and back to see the ids column is now visible', async() => {
const idVisible = await nightmare
.waitToClick(selectors.itemsIndex.searchResult)
.waitToClick(selectors.itemsIndex.goBackToModuleIndexButton)
.waitToClick(selectors.itemsIndex.searchIcon)
.wait(selectors.itemsIndex.searchResult)
.isVisible(selectors.itemsIndex.firstItemId);
expect(idVisible).toBeTruthy();
});
});

View File

@ -0,0 +1,48 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('Item regularize path', () => {
const nightmare = createNightmare();
beforeAll(() => {
nightmare
.loginAndModule('developer', 'item')
.accessToSearchResult(1)
.accessToSection('item.card.data');
});
it('should check the descriptor inactive icon is dark as the item is active', async() => {
let darkIcon = await nightmare
.wait(selectors.itemDescriptor.inactiveIcon)
.waitForClassNotPresent(selectors.itemDescriptor.inactiveIcon, 'bright')
.isVisible(selectors.itemDescriptor.inactiveIcon);
expect(darkIcon).toBeTruthy();
});
it('should set the item to inactive', async() => {
let result = await nightmare
.waitToClick(selectors.itemBasicData.isActiveCheckbox)
.waitToClick(selectors.itemBasicData.submitBasicDataButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
it('should reload the section and check the inactive icon is bright', async() => {
let brightIcon = await nightmare
.reloadSection('item.card.data')
.waitForClassPresent(selectors.itemDescriptor.inactiveIcon, 'bright')
.isVisible(selectors.itemDescriptor.inactiveIcon);
expect(brightIcon).toBeTruthy();
});
it('should set the item back to active', async() => {
let result = await nightmare
.waitToClick(selectors.itemBasicData.isActiveCheckbox)
.waitToClick(selectors.itemBasicData.submitBasicDataButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
});

View File

@ -7,7 +7,7 @@ describe('Ticket services path', () => {
beforeAll(() => {
nightmare
.loginAndModule('employee', 'ticket')
.accessToSearchResult('id:1')
.accessToSearchResult('1')
.accessToSection('ticket.card.service');
});
@ -24,7 +24,7 @@ describe('Ticket services path', () => {
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
}, 15000);
it('should confirm the service description was edited correctly', async() => {
const result = await nightmare

View File

@ -0,0 +1,35 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('pbx path', () => {
const nightmare = createNightmare();
beforeAll(() => {
nightmare
.loginAndModule('hr', 'worker')
.accessToSearchResult('employee')
.accessToSection('worker.card.pbx');
});
it('should receive an error when the extension exceeds 4 characters', async() => {
const result = await nightmare
.write(selectors.workerPbx.extensionInput, 55555)
.waitToClick(selectors.workerPbx.saveButton)
.waitForLastSnackbar();
expect(result).toEqual('EXTENSION_INVALID_FORMAT');
});
it('should sucessfully save the changes', async() => {
const result = await nightmare
.clearInput(selectors.workerPbx.extensionInput)
.write(selectors.workerPbx.extensionInput, 4444)
.clearInput(selectors.workerPbx.passwordInput)
.write(selectors.workerPbx.passwordInput, 666666)
.waitToClick(selectors.workerPbx.saveButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
});

View File

@ -6,7 +6,7 @@
<input
class="mdl-textfield__input"
type="time"
ng-model="$ctrl.value"
ng-model="$ctrl.model"
vn-validation="{{$ctrl.rule}}"
ng-disabled="$ctrl.disabled"
ng-readonly="$ctrl.readonly"

View File

@ -3,11 +3,19 @@ import Textfield from '../textfield/textfield';
import './style.scss';
export default class InputTime extends Textfield {
get value() {
return this._value;
}
get model() {
return this._model;
}
set model(value) {
this.value = value;
}
set value(value) {
if (!value) return;
@ -15,7 +23,15 @@ export default class InputTime extends Textfield {
newDate.setSeconds(0);
newDate.setMilliseconds(0);
this._value = newDate;
this._model = newDate;
this.hasValue = this._model !== null;
let date = new Date(value);
date.setSeconds(0);
date.setMilliseconds(0);
let offset = date.getTimezoneOffset() * 60000;
date.setTime(date.getTime() - offset);
this._value = date;
this.hasValue = this._value !== null;

View File

@ -1,25 +1,54 @@
<ul ng-if="$ctrl.items">
<ul ng-if="::$ctrl.items">
<li ng-repeat="item in $ctrl.items"
ng-class="{
'expanded': item.active,
'collapsed': !item.active,
'included': item.isIncluded == 1,
'excluded': item.isIncluded == 0
'included': item.selected == 1,
'excluded': item.selected == 0
}">
<vn-horizontal>
<vn-auto class="actions">
<vn-icon icon="keyboard_arrow_down"
ng-show="item.sons > 0"
<vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}"
ng-click="$ctrl.toggle(item, $event)">
</vn-icon>
</vn-auto>
<div class="description">
<vn-check vn-auto field="item.isIncluded"
on-change="$ctrl.select(item, value)" triple-state="true">
<vn-one class="description">
<vn-check vn-auto vn-acl="{{$ctrl.aclRole}}"
ng-if="$ctrl.selectable"
field="item.selected"
disabled="$ctrl.disabled"
on-change="$ctrl.select(item, value)"
triple-state="true">
</vn-check>
{{::item.name}}
</vn-one>
<vn-auto>
<vn-icon-button icon="{{icon.icon}}"
ng-repeat="icon in $ctrl.icons"
ng-click="$ctrl.onClick(icon, item, $ctrl.parent, $parent.$index)"
vn-acl="{{$ctrl.aclRole}}" vn-acl-action="remove">
</vn-icon-button>
</vn-auto>
</vn-horizontal>
<vn-treeview-child items="item.childs" parent="item"
selectable="$ctrl.selectable"
disabled="$ctrl.disabled"
editable="$ctrl.editable"
icons="$ctrl.icons"
acl-role="$ctrl.aclRole">
</vn-treeview-child>
</li>
<li ng-if="$ctrl.isInsertable && $ctrl.editable"
ng-click="$ctrl.onCreate($ctrl.parent)"
vn-acl="{{$ctrl.aclRole}}"
vn-acl-action="remove">
<vn-horizontal>
<vn-auto>
<vn-icon-button icon="add_circle"></vn-icon-button>
</vn-auto>
<div class="description" translate>
Create new one
</div>
</vn-horizontal>
<vn-treeview-child items="item.childs"></vn-treeview-child>
</li>
</ul>

View File

@ -4,6 +4,7 @@ import Component from '../../lib/component';
class Controller extends Component {
constructor($element, $scope) {
super($element, $scope);
this.$scope = $scope;
}
toggle(item) {
@ -13,13 +14,33 @@ class Controller extends Component {
select(item, value) {
this.treeview.onSelection(item, value);
}
onClick(icon, item, parent, index) {
let parentScope = this.$scope.$parent.$parent;
let parentController = parentScope.$ctrl;
icon.callback.call(parentController, item, parent, index);
}
onCreate(parent) {
this.treeview.onCreate(parent);
}
get isInsertable() {
return Array.isArray(this.parent) || this.parent.childs;
}
}
ngModule.component('vnTreeviewChild', {
template: require('./child.html'),
controller: Controller,
bindings: {
items: '<'
items: '<',
parent: '<',
icons: '<?',
disabled: '<?',
selectable: '<?',
editable: '<?',
aclRole: '<?',
},
require: {
treeview: '^vnTreeview'

View File

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

View File

@ -23,10 +23,19 @@ export default class Treeview extends Component {
});
}
/**
* Emits selection event
* @param {Object} item - Selected item
* @param {Boolean} value - Changed value
*/
onSelection(item, value) {
this.emit('selection', {item, value});
}
onCreate(parent) {
this.emit('create', {parent});
}
onToggle(item) {
if (item.active)
item.childs = undefined;
@ -45,12 +54,12 @@ export default class Treeview extends Component {
}
item.childs = newData.sort((a, b) => {
if (b.isIncluded !== a.isIncluded) {
if (a.isIncluded == null)
if (b.selected !== a.selected) {
if (a.selected == null)
return 1;
if (b.isIncluded == null)
if (b.selected == null)
return -1;
return b.isIncluded - a.isIncluded;
return b.selected - a.selected;
}
return a.name.localeCompare(b.name);
@ -68,6 +77,11 @@ ngModule.component('vnTreeview', {
template: require('./index.html'),
controller: Treeview,
bindings: {
model: '<'
model: '<',
icons: '<?',
disabled: '<?',
selectable: '<?',
editable: '<?',
aclRole: '@?'
}
});

View File

@ -21,7 +21,7 @@ vn-treeview {
}
li ul {
padding: 0 1.8em;
padding-left: 1.8em;
}
li > vn-horizontal {
@ -62,4 +62,8 @@ vn-treeview {
}
}
}
vn-icon-button {
padding: 0
}
}

View File

@ -36,7 +36,7 @@ function vnAcl(aclService, $timeout) {
return conditions;
}
function permissionElement($element, action) {
function permissionElement($scope, $element, action) {
if (!aclService.hasAny(acls)) {
if (action === 'disabled') {
let input = $element[0];
@ -72,9 +72,13 @@ function vnAcl(aclService, $timeout) {
priority: -1,
link: function($scope, $element, $attrs) {
acls = $attrs.vnAcl.split(',').map(i => i.trim());
if (acls[0] == '') return;
let action = $attrs.vnAclAction || 'disabled';
let conditions = getDynamicConditions($attrs);
permissionElement($element, action);
permissionElement($scope, $element, action);
if (Object.keys(conditions).length) {
let watchConditions = $scope.$watch(() => {
@ -82,7 +86,7 @@ function vnAcl(aclService, $timeout) {
let hasPermission = $scope.$eval($attrs[attrName]);
if (!hasPermission) {
updateAcls(conditions[attrName].role, hasPermission);
permissionElement($element, action);
permissionElement($scope, $element, action);
delete conditions[attrName];
}
});

View File

@ -40,3 +40,5 @@ December: Diciembre
Has delivery: Hay reparto
Loading: Cargando
Fields to show: Campos a mostrar
Create new one: Crear nuevo
Toggle: Desplegar/Plegar

View File

@ -1,9 +1,3 @@
<vn-crud-model
url="/api/Banks"
vn-id="banks"
data="banksData"
order="bank">
</vn-crud-model>
<vn-crud-model
url="/api/Warehouses"
vn-id="warehouses"
@ -33,11 +27,12 @@
label="Local bank"
id="localBank"
field="$ctrl.localBankFk"
data="banksData"
url="/api/Banks"
select-fields="['id','bank']"
show-field="bank"
order="id"
value-field="id">
value-field="id"
search-function="{or: [{id: $search}, {bank: {like: '%'+ $search +'%'}}]}">
<tpl-item>{{id}}: {{bank}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete

View File

@ -31,6 +31,13 @@ class Controller {
}
}
$onInit() {
if (window.localStorage.localBankFk && window.localStorage.localBankFk !== 'null')
window.localStorage.defaultBankFk = window.localStorage.localBankFk;
else
localStorage.removeItem('defaultBankFk');
}
set lang(value) {
this._lang = value;
this.$translate.use(value);
@ -105,7 +112,6 @@ class Controller {
}
show(event) {
this.$scope.banks.refresh();
this.$scope.warehouses.refresh();
this.$scope.companies.refresh();
this.$scope.popover.parent = event.target;
@ -139,11 +145,8 @@ class Controller {
});
}
$onInit() {
if (window.localStorage.localBankFk && window.localStorage.localBankFk !== 'null')
window.localStorage.defaultBankFk = window.localStorage.localBankFk;
else
localStorage.removeItem('defaultBankFk');
searchLocalBank(a, b) {
return angular.equals(a.id, b.id);
}
}

View File

@ -163,7 +163,7 @@ 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])) {
if (changedModelValue && (!ctx.instance || !ctx.instance[changedModelValue]) && ctx.where) {
changedModelId = [];
where = [];
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]}, options);

View File

@ -128,44 +128,33 @@ module.exports = function(Self) {
return replaceErrFunc(err);
}
function rewriteMethod(methodName) {
const realMethod = this[methodName];
return async(data, options, cb) => {
if (options instanceof Function) {
cb = options;
options = null;
}
try {
await realMethod.call(this, data, options);
if (cb) cb();
} catch (err) {
let myErr = replaceErr(err, replaceErrFunc);
if (cb)
cb(myErr);
else
throw myErr;
}
};
}
this.once('attached', () => {
let realUpsert = this.upsert;
this.upsert = async(data, options, cb) => {
if (options instanceof Function) {
cb = options;
options = null;
}
try {
await realUpsert.call(this, data, options);
if (cb) cb();
} catch (err) {
let myErr = replaceErr(err, replaceErrFunc);
if (cb)
cb(myErr);
else
throw myErr;
}
};
let realCreate = this.create;
this.create = async(data, options, cb) => {
if (options instanceof Function) {
cb = options;
options = null;
}
try {
await realCreate.call(this, data, options);
if (cb) cb();
} catch (err) {
let myErr = replaceErr(err, replaceErrFunc);
if (cb)
cb(myErr);
else
throw myErr;
}
};
this.remove =
this.deleteAll =
this.destroyAll = rewriteMethod.call(this, 'remove');
this.upsert = rewriteMethod.call(this, 'upsert');
this.create = rewriteMethod.call(this, 'create');
});
},

View File

@ -18,7 +18,6 @@
"Package cannot be blank": "Package cannot be blank",
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
"Cannot check VIES and Equalization Tax": "Cannot check VIES and Equalization Tax",
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
"You can't create an order for a frozen client": "You can't create an order for a frozen client",
"This address doesn't exist": "This address doesn't exist",
@ -39,5 +38,6 @@
"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",
"Street cannot be empty": "Street cannot be empty",
"City cannot be empty": "City cannot be empty"
"City cannot be empty": "City cannot be empty",
"EXTENSION_INVALID_FORMAT": "EXTENSION_INVALID_FORMAT"
}

View File

@ -59,7 +59,6 @@
"You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified",
"You must delete the claim id %d first": "Antes debes borrar la reclamacion %d",
"You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check VIES and Equalization Tax": "No puedes marcar VIES y RE al mismo",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no esta permitido el uso de la letra ñ",
@ -71,6 +70,8 @@
"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"
"Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Cuidad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento"
}

View File

@ -73,7 +73,7 @@ module.exports = Self => {
zg.rgt,
zg.depth,
zg.sons,
IF(ch.id = zg.id, isIncluded, null) isIncluded
IF(ch.id = zg.id, isIncluded, null) selected
FROM zoneGeo zg
JOIN tmp.checkedChilds ch
ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt
@ -86,7 +86,7 @@ module.exports = Self => {
child.rgt,
child.depth,
child.sons,
zi.isIncluded
zi.isIncluded AS selected
FROM zoneGeo parent
JOIN zoneGeo child ON child.lft > parent.lft
AND child.rgt < parent.rgt
@ -122,9 +122,11 @@ module.exports = Self => {
function nestLeaves(elements) {
elements.forEach(element => {
element.childs = Object.assign([], getLeaves(element));
let childs = Object.assign([], getLeaves(element));
if (childs.length > 0) {
element.childs = childs;
nestLeaves(element.childs);
}
});
}
@ -142,12 +144,12 @@ module.exports = Self => {
function sortNodes(nodes) {
return nodes.sort((a, b) => {
if (b.isIncluded !== a.isIncluded) {
if (a.isIncluded == null)
if (b.selected !== a.selected) {
if (a.selected == null)
return 1;
if (b.isIncluded == null)
if (b.selected == null)
return -1;
return b.isIncluded - a.isIncluded;
return b.selected - a.selected;
}
return a.name.localeCompare(b.name);

View File

@ -30,6 +30,10 @@
"bonus": {
"type": "Number",
"required": true
},
"isVolumetric": {
"type": "Boolean",
"required": true
}
},
"relations": {

View File

@ -62,6 +62,9 @@
display-controls="false">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-check field="$ctrl.zone.isVolumetric" label="Volumetric"></vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>

View File

@ -64,6 +64,9 @@
display-controls="false">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-check field="$ctrl.zone.isVolumetric" label="Volumetric"></vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Create"></vn-submit>

View File

@ -6,8 +6,8 @@ export default class Controller {
this.$state = $state;
this.zone = {
travelingDays: 0,
price: 0.50,
bonus: 0.50,
price: 0.20,
bonus: 0.20,
hour: new Date()
};
}

View File

@ -7,3 +7,4 @@ Delete zone: Eliminar zona
Are you sure you want to delete this zone?: ¿Estás seguro de querer eliminar esta zona?
Zones: Zonas
New zone: Nueva zona
Volumetric: Volumétrico

View File

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

View File

@ -29,22 +29,23 @@ module.exports = Self => {
}
});
Self.isValidClient = async function(id) {
Self.isValidClient = async id => {
let query =
`SELECT r.name
FROM salix.Account A
JOIN vn.client C ON A.id = C.id
JOIN salix.RoleMapping rm ON rm.principalId = A.id
FROM salix.Account a
JOIN vn.client c ON a.id = c.id
JOIN salix.RoleMapping rm ON rm.principalId = a.id
JOIN salix.Role r ON r.id = rm.roleId
WHERE A.id = ? AND C.isActive AND C.isTaxDataChecked`;
WHERE a.id = ? AND c.isActive AND c.isTaxDataChecked`;
let roleNames = await Self.rawSql(query, [id]);
if (!roleNames.length) return false;
roleNames.forEach(role => {
if (role.name === 'employee')
return false;
let isEmployee = roleNames.findIndex(role => {
return role.name === 'employee';
});
if (!roleNames.length || isEmployee > -1 ) return false;
return true;
};
};

View File

@ -1,57 +1,45 @@
const app = require('vn-loopback/server/server');
describe('Client isValidClient', () => {
it('should call the isValidClient() method with a client id and receive true', done => {
it('should call the isValidClient() method with a client id and receive true', async() => {
let id = 101;
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeTruthy();
done();
});
});
it('should call the isValidClient() method with a employee id and receive false', done => {
it('should call the isValidClient() method with an employee id to receive false', async() => {
let id = 1;
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeFalsy();
done();
});
});
it('should call the isValidClient() method with a unexistant id and receive false', done => {
it('should call the isValidClient() method with an unexistant id and receive false', async() => {
let id = 999999;
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeFalsy();
done();
});
});
it('should call the isValidClient() method with a invalid id and receive false', done => {
it('should call the isValidClient() method with an invalid id and receive false', async() => {
let id = 'Pepinillos';
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeFalsy();
done();
});
});
it('should call the isValidClient() method with a customer id which isnt active and return false', done => {
it('should call the isValidClient() method with a customer id which isnt active and return false', async() => {
let id = '106';
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeFalsy();
done();
});
});
it('should call the isValidClient() method with a customer id which his data isnt verified and return false', done => {
it('should call the isValidClient() method with a customer id which his data isnt verified and return false', async() => {
let id = '110';
app.models.Client.isValidClient(id)
.then(result => {
let result = await app.models.Client.isValidClient(id);
expect(result).toBeFalsy();
done();
});
});
});

View File

@ -73,6 +73,7 @@ module.exports = Self => {
Self.validateAsync('fi', tinIsValid, {
message: 'Invalid TIN'
});
let validateTin = require('../validations/validateTin');
async function tinIsValid(err, done) {
if (!this.isTaxDataChecked)
@ -85,7 +86,7 @@ module.exports = Self => {
let country = await Self.app.models.Country.findOne(filter);
let code = country ? country.code.toLowerCase() : null;
if (!validateTin(this.fi, code))
if (!this.fi || !validateTin(this.fi, code))
err();
done();
}

View File

@ -111,6 +111,12 @@
show-field="country">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Code"
model="$ctrl.newBankEntity.id">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Swift / BIC"

View File

@ -49,6 +49,7 @@ export default class Controller {
onBankEntityOpen() {
this.newBankEntity.name = '';
this.newBankEntity.id = '';
this.newBankEntity.bic = '';
this.$scope.$apply();
}
@ -58,6 +59,8 @@ export default class Controller {
try {
if (!this.newBankEntity.name)
throw new Error(`Name can't be empty`);
if (!this.newBankEntity.id)
throw new Error(`Code can't be empty`);
if (!this.newBankEntity.bic)
throw new Error(`Swift / BIC can't be empty`);

View File

@ -51,7 +51,8 @@ describe('Client', () => {
controller.newBankEntity = {
name: '',
bic: 'ES123',
countryFk: 1
countryFk: 1,
id: 999
};
controller.onBankEntityResponse('ACCEPT');
@ -62,18 +63,32 @@ describe('Client', () => {
controller.newBankEntity = {
name: 'My new bank entity',
bic: '',
countryFk: 1
countryFk: 1,
id: 999
};
controller.onBankEntityResponse('ACCEPT');
expect(vnApp.showError).toHaveBeenCalledWith(`Swift / BIC can't be empty`);
});
it(`should throw an error if id property is empty`, () => {
controller.newBankEntity = {
name: 'My new bank entity',
bic: 'ES123',
countryFk: 1,
id: null
};
controller.onBankEntityResponse('ACCEPT');
expect(vnApp.showError).toHaveBeenCalledWith(`Code can't be empty`);
});
it('should request to create a new bank entity', () => {
let newBankEntity = {
name: 'My new bank entity',
bic: 'ES123',
countryFk: 1
countryFk: 1,
id: 999
};
controller.newBankEntity = newBankEntity;
$httpBackend.when('POST', '/client/api/BankEntities').respond('done');

View File

@ -16,3 +16,4 @@ Save: Guardar
New bank entity: Nueva entidad bancaria
Name can't be empty: El nombre no puede quedar vacío
Swift / BIC can't be empty: El Swift / BIC no puede quedar vacío
Code: Código

View File

@ -89,12 +89,12 @@ module.exports = Self => {
}
});
filter = mergeFilters(ctx.args.filter, {where});
/* case 'hasVisible':
return value ? {'v.visible': {gt: 0}} : {'v.visible': {or: [{lte: 0}, {neq: null}]}}; */
filter = mergeFilters(ctx.args.filter, {where});
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,
@ -106,7 +106,7 @@ module.exports = Self => {
intr.description AS intrastat, i.stems,
ori.code AS origin, t.name AS type,
ic.name AS category, i.density,
b.grouping, b.packing, itn.code AS niche
b.grouping, b.packing, itn.code AS niche, @visibleCalc
FROM item i
LEFT JOIN itemType t ON t.id = i.typeFk
LEFT JOIN itemCategory ic ON ic.id = t.categoryFk
@ -120,13 +120,18 @@ module.exports = Self => {
LEFT JOIN itemPlacement itn ON itn.itemFk = i.id AND itn.warehouseFk = t.warehouseFk`
);
if (ctx.args.hasVisible === true) {
/* if (ctx.args.hasVisible !== undefined) {
stmts.push('CALL cache.visible_refresh(@visibleCalc, false, 1)');
stmts.push('CALL cache.visible_refresh(@visibleCalc, false, 44)');
let joinAvailable = new ParameterizedSQL(
`JOIN cache.visible v
ON v.item_id = i.id AND v.calc_id = @visibleCalc`
`LEFT JOIN cache.visible v
ON v.item_id = i.id`
);
stmt.merge(joinAvailable);
}
} */
/* where v.visible > 0
where v.visible <= 0 OR v.visible IS NULL
*/
if (ctx.args.tags) {
let i = 1;
@ -150,7 +155,12 @@ module.exports = Self => {
}
}
stmt.merge(conn.makeSuffix(filter));
stmt.merge(conn.makeWhere(filter.where));
/* if (ctx.args.hasVisible !== undefined)
stmt.merge(`GROUP BY i.id`);
*/
stmt.merge(conn.makePagination(filter));
let itemsIndex = stmts.push(stmt) - 1;
let sql = ParameterizedSQL.join(stmts, ';');

View File

@ -4,7 +4,12 @@ describe('item new()', () => {
let item;
afterAll(async done => {
await app.models.Item.destroyById(item.id);
let sql = 'DELETE FROM vn.itemLog WHERE originFk = ?';
await app.models.Item.rawSql(sql, [item.id]);
sql = 'DELETE FROM vn.item WHERE id = ?';
await app.models.Item.rawSql(sql, [item.id]);
done();
});

View File

@ -27,7 +27,7 @@ module.exports = Self => {
if (!tax.taxClassFk)
throw new UserError('Tax class cannot be blank');
promises.push(Self.app.models.ItemTaxCountry.updateAll(
promises.push(Self.app.models.ItemTaxCountry.update(
{id: tax.id},
{taxClassFk: tax.taxClassFk}
));

View File

@ -1,6 +1,11 @@
{
"name": "ItemBarcode",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "ItemLog",
"relation": "item",
"changedModelValue": "code"
},
"options": {
"mysql": {
"table": "itemBarcode"

View File

@ -1,6 +1,11 @@
{
"name": "ItemBotanical",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "ItemLog",
"relation": "item",
"changedModelValue": "botanical"
},
"options": {
"mysql": {
"table": "itemBotanical"

View File

@ -8,38 +8,48 @@
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
"type": "Number",
"forceId": false
},
"originFk": {
"type": "Number",
"required": true
},
"userFk": {
"type": "Number"
},
"action": {
"type": "String",
"required": true
},
"changedModel": {
"type": "String"
},
"oldInstance": {
"type": "Object"
},
"newInstance": {
"type": "Object"
},
"creationDate": {
"type": "Date"
},
"description": {
"changedModelId": {
"type": "Number"
},
"changedModelValue": {
"type": "String"
},
"action": {
"description": {
"type": "String"
}
},
"relations": {
"item": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "originFk"
},
"user": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "userFk"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -1,6 +1,11 @@
{
"name": "ItemNiche",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "ItemLog",
"relation": "item",
"changedModelValue": "code"
},
"options": {
"mysql": {
"table": "itemPlacement"

View File

@ -1,6 +1,11 @@
{
"name": "ItemTag",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "ItemLog",
"relation": "item",
"changedModelValue": "value"
},
"options": {
"mysql": {
"table": "itemTag"

View File

@ -1,6 +1,9 @@
{
"name": "Item",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"ItemLog"
},
"options": {
"mysql": {
"table": "item"

View File

@ -1,33 +0,0 @@
<vn-crud-model
vn-id="model"
url="/item/api/ItemLogs"
filter="::$ctrl.filter"
link="{originFk: $ctrl.$stateParams.id}"
limit="20"
data="logs" auto-load="false">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="description">Description</vn-th>
<vn-th field="action">Action</vn-th>
<vn-th field="userFk">Changed by</vn-th>
<vn-th field="creationDate" default-order="DESC">Date</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="itemLog in logs">
<vn-td>{{::itemLog.description}}</vn-td>
<vn-td>{{::itemLog.action}}</vn-td>
<vn-td>{{::itemLog.user.name}}</vn-td>
<vn-td>{{::itemLog.creationDate | dateTime:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-vertical>
<vn-pagination model="model"></vn-pagination>
</vn-card>
</vn-vertical>

View File

@ -1,25 +0,0 @@
import ngModule from '../module';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [{
relation: "item"
},
{
relation: "user",
scope: {
fields: ["name"]
}
}]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnItemHistory', {
template: require('./index.html'),
controller: Controller
});

View File

@ -11,7 +11,7 @@ import './data';
import './fetched-tags';
import './tags';
import './tax';
// import './history';
import './log';
import './last-entries';
import './niche';
import './botanical';

View File

@ -0,0 +1,9 @@
<vn-crud-model
vn-id="model"
url="/api/ItemLogs"
link="{originFk: $ctrl.$stateParams.id}"
filter="$ctrl.filter"
limit="20"
data="$ctrl.logs" auto-load="false">
</vn-crud-model>
<vn-log model="model"></vn-log>

View File

@ -0,0 +1,53 @@
import ngModule from '../module';
class Controller {
constructor($scope, $stateParams) {
this.$scope = $scope;
this.$stateParams = $stateParams;
this.filter = {
include: [{
relation: 'user',
scope: {
fields: ['name'],
},
}],
};
}
get logs() {
return this._logs;
}
set logs(value) {
this._logs = value;
if (this.logs) {
this.logs.forEach(log => {
log.oldProperties = this.getInstance(log.oldInstance);
log.newProperties = this.getInstance(log.newInstance);
});
}
}
getInstance(instance) {
let validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
const properties = [];
if (typeof instance == 'object' && instance != null) {
Object.keys(instance).forEach(property => {
if (validDate.test(instance[property]))
instance[property] = new Date(instance[property]).toLocaleString('es-ES');
properties.push({key: property, value: instance[property]});
});
return properties;
}
return null;
}
}
Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnItemLog', {
template: require('./index.html'),
controller: Controller,
});

View File

@ -12,7 +12,8 @@
{"state": "item.card.botanical", "icon": "local_florist"},
{"state": "item.card.itemBarcode", "icon": "icon-barcode"},
{"state": "item.card.diary", "icon": "icon-transaction"},
{"state": "item.card.last-entries", "icon": "icon-regentry"}
{"state": "item.card.last-entries", "icon": "icon-regentry"},
{"state": "item.card.log", "icon": "history"}
],
"keybindings": [
{"key": "a", "state": "item.index"}
@ -116,6 +117,11 @@
"item": "$ctrl.item"
},
"acl": ["employee"]
}, {
"url" : "/log",
"state": "item.card.log",
"component": "vn-item-log",
"description": "Log"
}
]
}

View File

@ -42,11 +42,6 @@
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
vn-one
label="With visible"
field="filter.hasVisible">
</vn-check>
<vn-check
vn-one
label="Active"

View File

@ -15,6 +15,11 @@ class Controller {
icon: 'icon-ticket',
state: `ticket.index({q: '{"orderFk": ${value.id}}'})`,
tooltip: 'Order ticket list'
},
btnTwo: {
icon: 'person',
state: `client.card.summary({id: ${value.clientFk}})`,
tooltip: 'Client card'
}
};
}

View File

@ -13,7 +13,7 @@ module.exports = Self => {
http: {source: 'query'}
}],
returns: {
type: ["Object"],
type: ['Object'],
root: true
},
http: {
@ -28,7 +28,8 @@ module.exports = Self => {
`SELECT name, itemFk, packagingFk
FROM (SELECT i.name, i.id itemFk, p.id packagingFk
FROM item i
JOIN packaging p ON i.id = p.itemFk) p`
JOIN packaging p ON i.id = p.itemFk
WHERE i.name <> '') p`
);
stmt.merge(conn.makeSuffix(filter));

View File

@ -1,7 +1,7 @@
let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('updateDiscount', {
Self.remoteMethodCtx('updateDiscount', {
description: 'Changes the discount of a sale',
accessType: '',
accepts: [{
@ -21,16 +21,12 @@ module.exports = Self => {
}
});
Self.updateDiscount = async params => {
Self.updateDiscount = async(ctx, params) => {
if (isNaN(params.editLines[0].discount))
throw new UserError(`The value should be a number`);
let model = Self.app.models;
let thisTicketIsEditable = await model.Ticket.isEditable(params.editLines[0].ticketFk);
if (!thisTicketIsEditable)
throw new UserError(`The sales of this ticket can't be modified`);
let ticket = await model.Ticket.find({
where: {
id: params.editLines[0].ticketFk
@ -41,9 +37,16 @@ module.exports = Self => {
fields: ['salesPersonFk']
}
}],
fields: ['id', 'clientFk']
fields: ['id', 'clientFk', 'refFk']
});
let userId = ctx.req.accessToken.userId;
let isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
if ((!thisTicketIsEditable && !isSalesAssistant) || (ticket.refFk && isSalesAssistant))
throw new UserError(`The sales of this ticket can't be modified`);
let componentToUse;
let usesMana = await model.WorkerMana.findOne({where: {workerFk: ticket[0].client().salesPersonFk}, fields: 'amount'});

View File

@ -23,23 +23,28 @@ module.exports = Self => {
}
});
Self.changeState = async(ctx, data) => {
Self.changeState = async(ctx, params) => {
let userId = ctx.req.accessToken.userId;
let $ = Self.app.models;
if (!data.stateFk)
if (!params.stateFk && !params.code)
throw new UserError('State cannot be blank');
if (params.code) {
let state = await $.State.findOne({where: {code: params.code}, fields: ['id']});
params.stateFk = state.id;
}
let isProduction = await $.Account.hasRole(userId, 'production');
let isSalesPerson = await $.Account.hasRole(userId, 'salesPerson');
let ticket = await $.TicketState.findById(
data.ticketFk,
params.ticketFk,
{fields: ['stateFk']}
);
let oldState = await $.State.findById(ticket.stateFk);
let newState = await $.State.findById(data.stateFk);
let newState = await $.State.findById(params.stateFk);
let isAllowed = isProduction || isSalesPerson
&& oldState.isEditable()
@ -50,9 +55,9 @@ module.exports = Self => {
if (newState.code != 'PICKER_DESIGNED') {
let worker = await $.Worker.findOne({where: {userFk: userId}});
data.workerFk = worker.id;
params.workerFk = worker.id;
}
return await $.TicketTracking.create(data);
return await $.TicketTracking.create(params);
};
};

View File

@ -220,12 +220,12 @@ module.exports = Self => {
SELECT
f.*,
tt.total,
tp.problem
tp.*
FROM tmp.filter f
LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.id
LEFT JOIN tmp.ticketTotal tt ON tt.ticketFk = f.id`);
stmt.merge(conn.makeOrderBy(filter.order));
let ticketsIndex = stmts.push(stmt) - 1;
let ticketsIndex = stmts.push(stmt);
stmts.push(
`DROP TEMPORARY TABLE

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
describe('ticket filter()', () => {
it('should call the filter method', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let ctx = {req: {accessToken: {userId: 9}}, args: {}};
let filter = {order: 'shipped DESC'};
let result = await app.models.Ticket.filter(ctx, filter);

View File

@ -11,7 +11,7 @@ class Controller {
{callback: this.showAddTurnDialog, name: 'Add turn', show: true},
{callback: this.showAddStowaway, name: 'Add stowaway', show: () => this.isTicketModule()},
{callback: this.showRemoveStowaway, name: 'Remove stowaway', show: () => this.shouldShowRemoveStowaway()},
/* {callback: this.showDeliveryNote, name: 'Show Delivery Note', show: true}, */
{callback: this.showDeliveryNote, name: 'Show Delivery Note', show: true},
{callback: this.showDeleteTicketDialog, name: 'Delete ticket', show: true},
/* callback: this.showChangeShipped, name: 'Change shipped hour', show: true} */
];
@ -57,7 +57,8 @@ class Controller {
}
goToTicket(ticketID) {
this.$state.go('ticket.card.sale', {id: ticketID});
let url = this.$state.href('ticket.card.sale', {id: ticketID}, {absolute: true});
window.open(url, '_blank');
}
onMoreOpen() {

View File

@ -52,10 +52,28 @@
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td shrink>
<vn-icon
ng-show="ticket.problem"
ng-show="ticket.hasTicketRequest"
class="bright"
vn-tooltip="{{ticket.problem}}"
icon="warning">
vn-tooltip="{{ticket.hasTicketRequest}}"
icon="icon-100">
</vn-icon>
<vn-icon
ng-show="ticket.isAvailable === 0"
class="bright"
vn-tooltip="{{ticket.isAvailable}}"
icon="icon-unavailable">
</vn-icon>
<vn-icon
ng-show="ticket.isFreezed"
class="bright"
vn-tooltip="Client frozen"
icon="icon-frozen">
</vn-icon>
<vn-icon
ng-show="ticket.risk"
class="bright"
vn-tooltip="Risk : {{ticket.risk}}"
icon="icon-risk">
</vn-icon>
</vn-td>
<vn-td number>{{::ticket.id}}</vn-td>

View File

@ -35,12 +35,12 @@
rule="ticketObservation.description">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
<vn-icon-button
pointer
vn-tooltip="Remove note"
icon="delete"
ng-click="model.remove($index)">
</vn-icon>
</vn-icon-button>
</vn-auto>
</vn-horizontal>
</vn-one>

View File

@ -39,12 +39,12 @@
ng-readonly="true">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
<vn-icon-button
pointer
vn-tooltip="Remove package"
icon="delete"
ng-click="model.remove($index)">
</vn-icon>
</vn-icon-button>
</vn-one>
</vn-horizontal>
</vn-one>

View File

@ -27,12 +27,13 @@
model="service.quantity"
rule="TicketService.quantity">
</vn-textfield>
<vn-textfield
<vn-input-number
vn-one
step="1"
label="Price"
model="service.price"
rule="TicketService.price">
</vn-textfield>
display-controls="false">
</vn-input-number>
<vn-autocomplete vn-one
url="/api/TaxClasses"
label="Tax class"

View File

@ -17,12 +17,7 @@ class Controller {
set ticket(value) {
this._ticket = value;
if (!value) return;
this.$http.get(`/ticket/api/Tickets/${this.ticket.id}/summary`).then(res => {
if (res && res.data)
this.summary = res.data;
});
if (value) this.getSummary();
}
get formattedAddress() {
@ -34,6 +29,13 @@ class Controller {
return `${address.street} - ${address.city} ${province}`;
}
getSummary() {
this.$http.get(`/ticket/api/Tickets/${this.ticket.id}/summary`).then(res => {
if (res && res.data)
this.summary = res.data;
});
}
showDescriptor(event, itemFk) {
this.quicklinks = {
btnThree: {
@ -64,25 +66,22 @@ class Controller {
}
setOkState() {
let filter = {where: {code: 'OK'}, fields: ['id']};
let json = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`/ticket/api/States?filter=${json}`).then(res => {
this.changeTicketState(res.data[0].id);
});
}
changeTicketState(value) {
let params;
let params = {};
if (this.$state.params.id)
params = {ticketFk: this.$state.params.id, stateFk: value};
params = {ticketFk: this.$state.params.id};
if (!this.$state.params.id)
params = {ticketFk: this.ticket.id, stateFk: value};
params = {ticketFk: this.ticket.id};
params.code = 'OK';
this.$http.post(`/ticket/api/TicketTrackings/changeState`, params).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
if (this.card) this.card.reload();
if (this.card)
this.card.reload();
else
this.getSummary();
});
}
}

View File

@ -0,0 +1,87 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('getLeaves', {
description: 'Returns the first shipped and landed possible for params',
accessType: '',
accepts: [{
arg: 'parentFk',
type: 'Number',
default: 1,
required: false,
},
{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getLeaves`,
verb: 'GET'
}
});
Self.getLeaves = async(parentFk = 1, filter) => {
let conn = Self.dataSource.connector;
let stmt = new ParameterizedSQL(
`SELECT
child.id,
child.name,
child.lft,
child.rgt,
child.depth,
child.sons
FROM department parent
JOIN department child ON child.lft > parent.lft
AND child.rgt < parent.rgt
AND child.depth = parent.depth + 1
WHERE parent.id = ?`, [parentFk]);
// Get nodes from depth greather than Origin
stmt.merge(conn.makeSuffix(filter));
const nodes = await Self.rawStmt(stmt);
if (nodes.length == 0)
return nodes;
// Get parent nodes
const minorDepth = nodes.reduce((a, b) => {
return b < a ? b : a;
}).depth;
const parentNodes = nodes.filter(element => {
return element.depth === minorDepth;
});
const leaves = Object.assign([], parentNodes);
nestLeaves(leaves);
function nestLeaves(elements) {
elements.forEach(element => {
let childs = Object.assign([], getLeaves(element));
if (childs.length > 0) {
element.childs = childs;
nestLeaves(element.childs);
}
});
}
function getLeaves(parent) {
let elements = nodes.filter(element => {
return element.lft > parent.lft && element.rgt < parent.rgt
&& element.depth === parent.depth + 1;
});
return elements;
}
return leaves;
};
};

View File

@ -0,0 +1,43 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('nodeAdd', {
description: 'Returns the first shipped and landed possible for params',
accessType: '',
accepts: [{
arg: 'parentFk',
type: 'Number',
required: false,
},
{
arg: 'name',
type: 'String',
required: true,
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/nodeAdd`,
verb: 'POST'
}
});
Self.nodeAdd = async(parentFk = 1, name) => {
let stmts = [];
let conn = Self.dataSource.connector;
let nodeIndex = stmts.push(new ParameterizedSQL(
`CALL nst.NodeAdd('vn', 'department', ?, ?)`, [parentFk, name])) - 1;
stmts.push(`CALL nst.nodeRecalc('vn', 'department')`);
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
let [node] = result[nodeIndex];
return node;
};
};

View File

@ -0,0 +1,29 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('nodeDelete', {
description: 'Returns the first shipped and landed possible for params',
accessType: '',
accepts: [{
arg: 'parentFk',
type: 'Number',
required: false,
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/nodeDelete`,
verb: 'POST'
}
});
Self.nodeDelete = async parentFk => {
let stmt = new ParameterizedSQL(
`CALL nst.nodeDelete('vn', 'department', ?)`, [parentFk]);
return await Self.rawStmt(stmt);
};
};

View File

@ -80,7 +80,8 @@ module.exports = Self => {
? {'w.id': value}
: {or: [
{'w.firstName': {like: `%${value}%`}},
{'w.name': {like: `%${value}%`}}
{'w.name': {like: `%${value}%`}},
{'u.name': {like: `%${value}%`}}
]};
case 'id':
return {'w.id': value};

View File

@ -0,0 +1,19 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/department/getLeaves')(Self);
require('../methods/department/nodeAdd')(Self);
require('../methods/department/nodeDelete')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_ROW_IS_REFERENCED_2')
return new UserError(`You cannot remove this department`);
return err;
});
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`The department name can't be repeated`);
return err;
});
};

View File

@ -22,7 +22,9 @@ class Controller {
},
{
relation: 'sip',
scope: {fields: ['extension']}
scope: {
fields: ['extension', 'secret']
}
}, {
relation: 'department',
scope: {

View File

@ -0,0 +1,45 @@
<vn-crud-model
vn-id="model"
url="/worker/api/departments/getLeaves"
params="::$ctrl.params"
auto-load="false">
</vn-crud-model>
<form name="form">
<div margin-medium>
<vn-card margin-medium-v pad-medium>
<vn-treeview vn-id="treeview" model="model"
on-selection="$ctrl.onSelection(item, value)"
on-create="$ctrl.onCreate(parent)"
icons="$ctrl.icons" editable="true" acl-role="hr">
</vn-treeview>
</vn-card>
</div>
</form>
<vn-confirm
vn-id="deleteNode"
on-response="$ctrl.onRemoveResponse(response)"
question="Delete department"
message="Are you sure you want to delete it?">
</vn-confirm>
<!-- Create department dialog -->
<vn-dialog
vn-id="createNode"
on-open="$ctrl.onCreateDialogOpen()"
on-response="$ctrl.onCreateResponse(response)">
<tpl-body>
<h5 pad-small-v translate>New department</h5>
<vn-horizontal>
<vn-textfield vn-one
label="Name"
model="$ctrl.newNode.name">
</vn-textfield>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="CANCEL" translate-attr="{value: 'Cancel'}"/>
<button response="ACCEPT" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,78 @@
import ngModule from '../module';
class Controller {
constructor($scope, $http, vnApp, $translate) {
this.$scope = $scope;
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
this.params = {parentFk: 1};
this.icons = [{icon: 'delete', tooltip: 'Delete', callback: this.onDelete}];
this.newNode = {
name: ''
};
}
onCreate(parent) {
if (parent instanceof Object)
this.newNode.parentFk = parent.id;
this.selectedNode = {parent};
this.$scope.createNode.show();
}
onDelete(item, parent, index) {
this.selectedNode = {id: item.id, parent, index};
this.$scope.deleteNode.show();
}
onCreateDialogOpen() {
this.newNode.name = '';
}
onCreateResponse(response) {
if (response == 'ACCEPT') {
try {
if (!this.newNode.name)
throw new Error(`Name can't be empty`);
this.$http.post(`/worker/api/Departments/nodeAdd`, this.newNode).then(response => {
if (response.data) {
let parent = this.selectedNode.parent;
if ((parent instanceof Object) && !(parent instanceof Array)) {
const childs = parent.childs;
childs.push(response.data);
} else if ((parent instanceof Object) && (parent instanceof Array))
parent.push(response.data);
}
});
} catch (e) {
this.vnApp.showError(this.$translate.instant(e.message));
return false;
}
}
return true;
}
onRemoveResponse(response) {
if (response === 'ACCEPT') {
const path = `/worker/api/Departments/nodeDelete`;
this.$http.post(path, {parentFk: this.selectedNode.id}).then(() => {
let parent = this.selectedNode.parent;
if ((parent instanceof Object) && !(parent instanceof Array)) {
const childs = parent.childs;
childs.splice(this.selectedNode.index, 1);
} else if ((parent instanceof Object) && (parent instanceof Array))
parent.splice(this.selectedNode.index, 1);
});
}
}
}
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];
ngModule.component('vnWorkerDepartment', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,3 @@
New department: Nuevo departamento
Delete department: Eliminar departamento
Are you sure you want to delete it?: ¿Seguro que quieres eliminarlo?

View File

@ -7,3 +7,5 @@ import './descriptor';
import './descriptor-popover';
import './search-panel';
import './basic-data';
import './pbx';
import './department';

View File

@ -8,17 +8,30 @@
<div class="content-block">
<div class="vn-list">
<vn-card pad-medium-h>
<vn-horizontal>
<vn-searchbar
style="width: 100%"
panel="vn-worker-search-panel"
on-search="$ctrl.onSearch($params)"
vn-focus>
</vn-searchbar>
<vn-icon-menu
vn-id="more-button"
icon="more_vert"
show-filter="false"
value-field="callback"
translate-fields="['name']"
data="$ctrl.moreOptions"
on-change="$ctrl.onMoreChange(value)">
</vn-icon-menu>
</vn-horizontal>
</vn-card>
<vn-card margin-medium-v>
<a ng-repeat="worker in workers track by worker.id"
<a
ng-repeat="worker in workers track by worker.id"
ui-sref="worker.card.summary({id: worker.id})"
translate-attr="{title: 'View worker'}"
class="vn-list-item">
class="vn-list-item searchResult">
<vn-horizontal>
<vn-one>
<h6>{{::worker.nickname}}</h6>

View File

@ -1,11 +1,16 @@
import ngModule from '../module';
import './style.scss';
export default class Controller {
constructor($) {
constructor($, $state) {
this.$state = $state;
Object.assign(this, {
$,
selectedWorker: null,
});
this.moreOptions = [
{callback: () => this.$state.go('worker.department'), name: 'Departments'}
];
}
onSearch(params) {
@ -22,9 +27,13 @@ export default class Controller {
this.$.preview.show();
event.stopImmediatePropagation();
}
onMoreChange(callback) {
callback.call(this);
}
}
Controller.$inject = ['$scope'];
Controller.$inject = ['$scope', '$state'];
ngModule.component('vnWorkerIndex', {
template: require('./index.html'),

View File

@ -0,0 +1,11 @@
@import "variables";
vn-worker-index vn-icon-menu {
padding-top: 30px;
padding-left: 10px;
color: $color-main;
li {
color: initial;
}
}

View File

@ -8,7 +8,9 @@ User id: Id de usuario
Role: Rol
Extension: Extensión
Go to client: Ir al cliente
Private Branch Exchange: Centralita
View worker: Ver trabajador
Worker id: Id trabajador
Fiscal Identifier: NIF
User name: Usuario
Departments: Departamentos

View File

@ -0,0 +1,28 @@
<mg-ajax path="/api/Sips/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.worker.sip"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="watcher.submit()" compact>
<vn-card pad-large>
<vn-vertical>
<vn-horizontal>
<vn-textfield
vn-one
label="Extension"
model="$ctrl.worker.sip.extension">
</vn-textfield>
<vn-textfield
vn-one
label="Password"
model="$ctrl.worker.sip.secret">
</vn-textfield>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,8 @@
import ngModule from '../module';
ngModule.component('vnWorkerPbx', {
template: require('./index.html'),
bindings: {
worker: '<'
}
});

View File

@ -4,7 +4,8 @@
"icon" : "icon-worker",
"validations" : true,
"menu": [
{"state": "worker.card.basicData", "icon": "settings"}
{"state": "worker.card.basicData", "icon": "settings"},
{"state": "worker.card.pbx", "icon": ""}
],
"routes": [
{
@ -32,7 +33,8 @@
"component": "vn-worker-card",
"abstract": true,
"description": "Detail"
}, {
},
{
"url": "/basic-data",
"state": "worker.card.basicData",
"component": "vn-worker-basic-data",
@ -41,6 +43,21 @@
"worker": "$ctrl.worker"
},
"acl": ["developer"]
}, {
"url": "/pbx",
"state": "worker.card.pbx",
"component": "vn-worker-pbx",
"description": "Private Branch Exchange",
"params": {
"worker": "$ctrl.worker"
},
"acl": ["hr"]
},
{
"url" : "/department",
"state": "worker.department",
"component": "vn-worker-department",
"description": "Departments"
}
]
}

228
package-lock.json generated
View File

@ -2735,15 +2735,6 @@
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
"combine-lists": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz",
"integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
"dev": true,
"requires": {
"lodash": "^4.5.0"
}
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@ -2886,6 +2877,18 @@
"xdg-basedir": "^3.0.0"
}
},
"connect": {
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
"integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
"dev": true,
"requires": {
"debug": "2.6.9",
"finalhandler": "1.1.0",
"parseurl": "~1.3.2",
"utils-merge": "1.0.1"
}
},
"connect-history-api-fallback": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz",
@ -3287,9 +3290,9 @@
"integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg="
},
"date-format": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
"integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz",
"integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==",
"dev": true
},
"date-now": {
@ -4442,40 +4445,6 @@
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
"dev": true
},
"expand-braces": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz",
"integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
"dev": true,
"requires": {
"array-slice": "^0.2.3",
"array-unique": "^0.2.1",
"braces": "^0.1.2"
},
"dependencies": {
"array-slice": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
"integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
"dev": true
},
"array-unique": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
"integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
"dev": true
},
"braces": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
"integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
"dev": true,
"requires": {
"expand-range": "^0.1.0"
}
}
}
},
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@ -4511,30 +4480,6 @@
}
}
},
"expand-range": {
"version": "0.1.1",
"resolved": "http://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
"integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
"dev": true,
"requires": {
"is-number": "^0.1.1",
"repeat-string": "^0.2.2"
},
"dependencies": {
"is-number": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz",
"integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=",
"dev": true
},
"repeat-string": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz",
"integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=",
"dev": true
}
}
},
"expand-tilde": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@ -4932,6 +4877,29 @@
}
}
},
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
"integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
"dev": true,
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.1",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.3.1",
"unpipe": "~1.0.0"
},
"dependencies": {
"statuses": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
"integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
"dev": true
}
}
},
"find-cache-dir": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
@ -7729,28 +7697,27 @@
"dev": true
},
"karma": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz",
"integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/karma/-/karma-4.0.1.tgz",
"integrity": "sha512-ind+4s03BqIXas7ZmraV3/kc5+mnqwCd+VDX1FndS6jxbt03kQKX2vXrWxNLuCjVYmhMwOZosAEKMM0a2q7w7A==",
"dev": true,
"requires": {
"bluebird": "^3.3.0",
"body-parser": "^1.16.1",
"braces": "^2.3.2",
"chokidar": "^2.0.3",
"colors": "^1.1.0",
"combine-lists": "^1.0.0",
"connect": "^3.6.0",
"core-js": "^2.2.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.0",
"expand-braces": "^0.1.1",
"flatted": "^2.0.0",
"glob": "^7.1.1",
"graceful-fs": "^4.1.2",
"http-proxy": "^1.13.0",
"isbinaryfile": "^3.0.0",
"lodash": "^4.17.5",
"log4js": "^3.0.0",
"lodash": "^4.17.11",
"log4js": "^4.0.0",
"mime": "^2.3.1",
"minimatch": "^3.0.2",
"optimist": "^0.6.1",
@ -7764,33 +7731,6 @@
"useragent": "2.3.0"
},
"dependencies": {
"connect": {
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
"integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
"dev": true,
"requires": {
"debug": "2.6.9",
"finalhandler": "1.1.0",
"parseurl": "~1.3.2",
"utils-merge": "1.0.1"
}
},
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
"integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
"dev": true,
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.1",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.3.1",
"unpipe": "~1.0.0"
}
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
@ -7805,12 +7745,6 @@
"path-is-absolute": "^1.0.0"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"mime": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
@ -7822,18 +7756,6 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"statuses": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
"integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
"dev": true
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true
}
}
},
@ -8259,24 +8181,18 @@
}
},
"log4js": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz",
"integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-4.0.2.tgz",
"integrity": "sha512-KE7HjiieVDPPdveA3bJZSuu0n8chMkFl8mIoisBFxwEJ9FmXe4YzNuiqSwYUiR1K8q8/5/8Yd6AClENY1RA9ww==",
"dev": true,
"requires": {
"circular-json": "^0.5.5",
"date-format": "^1.2.0",
"date-format": "^2.0.0",
"debug": "^3.1.0",
"flatted": "^2.0.0",
"rfdc": "^1.1.2",
"streamroller": "0.7.0"
"streamroller": "^1.0.1"
},
"dependencies": {
"circular-json": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
"integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
"dev": true
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
@ -12918,7 +12834,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
"integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
"dev": true,
"requires": {
"lodash": "^4.17.11"
}
@ -12927,7 +12842,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
"integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
"dev": true,
"requires": {
"request-promise-core": "1.1.2",
"stealthy-require": "^1.1.1",
@ -14070,8 +13984,7 @@
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
"dev": true
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"stream-browserify": {
"version": "2.0.2",
@ -14164,17 +14077,27 @@
"dev": true
},
"streamroller": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
"integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.3.tgz",
"integrity": "sha512-P7z9NwP51EltdZ81otaGAN3ob+/F88USJE546joNq7bqRNTe6jc74fTBDyynxP4qpIfKlt/CesEYicuMzI0yJg==",
"dev": true,
"requires": {
"date-format": "^1.2.0",
"async": "^2.6.1",
"date-format": "^2.0.0",
"debug": "^3.1.0",
"mkdirp": "^0.5.1",
"readable-stream": "^2.3.0"
"fs-extra": "^7.0.0",
"lodash": "^4.17.10"
},
"dependencies": {
"async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"dev": true,
"requires": {
"lodash": "^4.17.11"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
@ -14184,6 +14107,17 @@
"ms": "^2.1.1"
}
},
"fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
@ -15532,6 +15466,12 @@
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
"dev": true
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",

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