diff --git a/db/changes/10320-monitors/00-sale_getProblems.sql b/db/changes/10320-monitors/00-sale_getProblems.sql
new file mode 100644
index 000000000..290dcddb2
--- /dev/null
+++ b/db/changes/10320-monitors/00-sale_getProblems.sql
@@ -0,0 +1,193 @@
+DROP PROCEDURE IF EXISTS `vn`.`sale_getProblems`;
+
+DELIMITER $$
+$$
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`sale_getProblems`(IN vIsTodayRelative TINYINT(1))
+BEGIN
+/**
+ * Calcula los problemas de cada venta
+ * para un conjunto de tickets.
+ *
+ * @table tmp.sale_getProblems(ticketFk, clientFk, warehouseFk, shipped) Identificadores de los tickets a calcular
+ * @return tmp.sale_problems
+ */
+ DECLARE vWarehouse INT;
+ DECLARE vDate DATE;
+ DECLARE vAvailableCache INT;
+ DECLARE vDone INT DEFAULT 0;
+ DECLARE vComponentCount INT;
+
+ DECLARE vCursor CURSOR FOR
+ SELECT DISTINCT tt.warehouseFk, IF(vIsTodayRelative, CURDATE(), date(tt.shipped))
+ FROM tmp.sale_getProblems tt
+ WHERE DATE(tt.shipped) BETWEEN CURDATE()
+ AND TIMESTAMPADD(DAY, IF(vIsTodayRelative, 9.9, 1.9), CURDATE());
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale_problems;
+ CREATE TEMPORARY TABLE tmp.sale_problems (
+ ticketFk INT(11),
+ saleFk INT(11),
+ isFreezed INTEGER(1) DEFAULT 0,
+ risk DECIMAL(10,2) DEFAULT 0,
+ hasTicketRequest INTEGER(1) DEFAULT 0,
+ isAvailable INTEGER(1) DEFAULT 1,
+ itemShortage VARCHAR(250),
+ isTaxDataChecked INTEGER(1) DEFAULT 1,
+ itemDelay VARCHAR(250),
+ hasComponentLack INTEGER(1),
+ PRIMARY KEY (ticketFk, saleFk)
+ ) ENGINE = MEMORY;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticket_list;
+ CREATE TEMPORARY TABLE tmp.ticket_list
+ (PRIMARY KEY (ticketFk))
+ ENGINE = MEMORY
+ SELECT tp.ticketFk, c.id clientFk
+ FROM tmp.sale_getProblems tp
+ JOIN vn.client c ON c.id = tp.clientFk;
+
+ SELECT COUNT(*) INTO vComponentCount
+ FROM vn.component c
+ WHERE c.isRequired;
+
+ INSERT INTO tmp.sale_problems(ticketFk, hasComponentLack, saleFk)
+ SELECT tl.ticketFk, (COUNT(DISTINCT s.id) * vComponentCount > COUNT(c.id)), s.id
+ FROM tmp.ticket_list tl
+ JOIN vn.sale s ON s.ticketFk = tl.ticketFk
+ LEFT JOIN vn.saleComponent sc ON sc.saleFk = s.id
+ LEFT JOIN vn.component c ON c.id = sc.componentFk AND c.isRequired
+ GROUP BY tl.ticketFk, s.id;
+
+ INSERT INTO tmp.sale_problems(ticketFk, isFreezed)
+ SELECT DISTINCT tl.ticketFk, TRUE
+ FROM tmp.ticket_list tl
+ JOIN vn.client c ON c.id = tl.clientFk
+ WHERE c.isFreezed
+ ON DUPLICATE KEY UPDATE
+ isFreezed = c.isFreezed;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.clientGetDebt;
+ CREATE TEMPORARY TABLE tmp.clientGetDebt
+ (PRIMARY KEY (clientFk))
+ ENGINE = MEMORY
+ SELECT DISTINCT clientFk
+ FROM tmp.ticket_list;
+
+ CALL clientGetDebt(CURDATE());
+
+ INSERT INTO tmp.sale_problems(ticketFk, risk)
+ SELECT DISTINCT tl.ticketFk, r.risk
+ FROM tmp.ticket_list 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
+ JOIN vn.clientConfig cc
+ WHERE r.risk - cc.riskTolerance > c.credit + 10
+ AND a.isRiskFree = FALSE
+ ON DUPLICATE KEY UPDATE
+ risk = r.risk;
+
+ INSERT INTO tmp.sale_problems(ticketFk, hasTicketRequest)
+ SELECT DISTINCT tl.ticketFk, TRUE
+ FROM tmp.ticket_list tl
+ JOIN vn.ticketRequest tr ON tr.ticketFk = tl.ticketFk
+ WHERE tr.isOK IS NULL
+ ON DUPLICATE KEY UPDATE
+ hasTicketRequest = TRUE;
+
+ OPEN vCursor;
+
+ WHILE NOT vDone
+ DO
+ FETCH vCursor INTO vWarehouse, vDate;
+
+ CALL cache.available_refresh(vAvailableCache, FALSE, vWarehouse, vDate);
+
+ INSERT INTO tmp.sale_problems(ticketFk, isAvailable, saleFk)
+ SELECT tl.ticketFk, FALSE, s.id
+ FROM tmp.ticket_list tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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.available av ON av.item_id = i.id
+ AND av.calc_id = vAvailableCache
+ WHERE date(t.shipped) = vDate
+ AND it.categoryFk != 6
+ 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 = FALSE, saleFk = VALUE(saleFk);
+
+ INSERT INTO tmp.sale_problems(ticketFk, itemShortage, saleFk)
+ SELECT ticketFk, problem, saleFk
+ FROM (
+ SELECT tl.ticketFk, CONCAT('F: ',GROUP_CONCAT(i.id, ' ', i.longName, ' ')) problem, s.id AS saleFk
+ FROM tmp.ticket_list tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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 vn.itemShelvingStock_byWarehouse issw ON issw.itemFk = i.id AND issw.warehouseFk = t.warehouseFk
+ 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(issw.visible, 0)
+ AND s.quantity > 0
+ AND s.isPicked = FALSE
+ AND s.reserved = FALSE
+ AND it.categoryFk != 6
+ AND IF(vIsTodayRelative, TRUE, date(t.shipped) = vDate)
+ AND NOT i.generic
+ AND CURDATE() = vDate
+ AND t.warehouseFk = vWarehouse
+ GROUP BY tl.ticketFk LIMIT 1) sub
+ ON DUPLICATE KEY UPDATE
+ itemShortage = sub.problem, saleFk = sub.saleFk;
+
+ INSERT INTO tmp.sale_problems(ticketFk, itemDelay, saleFk)
+ SELECT ticketFk, problem, saleFk
+ FROM (
+ SELECT tl.ticketFk, GROUP_CONCAT('I: ',i.id, ' ', i.longName, ' ') problem, s.id AS saleFk
+ FROM tmp.ticket_list tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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 vn.itemShelvingStock_byWarehouse issw ON issw.itemFk = i.id AND issw.warehouseFk = t.warehouseFk
+ WHERE s.quantity > IFNULL(issw.visible, 0)
+ AND s.quantity > 0
+ AND s.isPicked = FALSE
+ AND s.reserved = FALSE
+ AND it.categoryFk != 6
+ AND IF(vIsTodayRelative, TRUE, date(t.shipped) = vDate)
+ AND NOT i.generic
+ AND CURDATE() = vDate
+ AND t.warehouseFk = vWarehouse
+ GROUP BY tl.ticketFk LIMIT 1) sub
+ ON DUPLICATE KEY UPDATE
+ itemDelay = sub.problem, saleFk = sub.saleFk;
+ END WHILE;
+
+ CLOSE vCursor;
+
+ INSERT INTO tmp.sale_problems(ticketFk, isTaxDataChecked)
+ SELECT DISTINCT tl.ticketFk, FALSE
+ FROM tmp.ticket_list tl
+ JOIN vn.client c ON c.id = tl.clientFk
+ WHERE c.isTaxDataChecked = FALSE
+ ON DUPLICATE KEY UPDATE
+ isTaxDataChecked = FALSE;
+
+ DROP TEMPORARY TABLE
+ tmp.clientGetDebt,
+ tmp.ticket_list;
+END;;$$
+DELIMITER ;
+
diff --git a/db/changes/10320-monitors/00-sale_getProblemsByTicket.sql b/db/changes/10320-monitors/00-sale_getProblemsByTicket.sql
new file mode 100644
index 000000000..1f30014d4
--- /dev/null
+++ b/db/changes/10320-monitors/00-sale_getProblemsByTicket.sql
@@ -0,0 +1,30 @@
+DROP PROCEDURE IF EXISTS `vn`.`sale_getProblemsByTicket`;
+
+DELIMITER $$
+$$
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`sale_getProblemsByTicket`(IN vTicketFk INT, IN vIsTodayRelative TINYINT(1))
+BEGIN
+/**
+ * Calcula los problemas de cada venta
+ * para un conjunto de tickets.
+ *
+ * @return Problems result
+ */
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems;
+ CREATE TEMPORARY TABLE tmp.sale_getProblems
+ (INDEX (ticketFk))
+ ENGINE = MEMORY
+ SELECT t.id ticketFk, t.clientFk, t.warehouseFk, t.shipped
+ FROM ticket t
+ WHERE t.id = vTicketFk;
+
+ CALL sale_getProblems(vIsTodayRelative);
+
+ SELECT * FROM tmp.sale_problems;
+
+ DROP TEMPORARY TABLE
+ tmp.sale_getProblems,
+ tmp.sale_problems;
+END;;$$
+DELIMITER ;
diff --git a/db/changes/10320-monitors/00-ticketGetProblems.sql b/db/changes/10320-monitors/00-ticketGetProblems.sql
new file mode 100644
index 000000000..85d6e4018
--- /dev/null
+++ b/db/changes/10320-monitors/00-ticketGetProblems.sql
@@ -0,0 +1,188 @@
+DROP PROCEDURE IF EXISTS `vn`.`ticketGetProblems`;
+
+DELIMITER $$
+$$
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`ticketGetProblems`(IN vIsTodayRelative TINYINT(1))
+BEGIN
+/**
+ * @deprecated Use ticket_getProblems() instead
+ *
+ */
+ DECLARE vWarehouse INT;
+ DECLARE vDate DATE;
+ DECLARE vAvailableCache INT;
+ DECLARE vDone INT DEFAULT 0;
+ DECLARE vComponentCount INT;
+
+ DECLARE vCursor CURSOR FOR
+ SELECT DISTINCT tt.warehouseFk, IF(vIsTodayRelative, CURDATE(), date(tt.shipped))
+ FROM tmp.ticketGetProblems tt
+ WHERE DATE(tt.shipped) BETWEEN CURDATE()
+ AND TIMESTAMPADD(DAY, IF(vIsTodayRelative, 9.9, 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,
+ itemShortage VARCHAR(250),
+ isTaxDataChecked INTEGER(1) DEFAULT 1,
+ itemDelay VARCHAR(250),
+ componentLack INTEGER(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;
+
+ SELECT COUNT(*) INTO vComponentCount
+ FROM vn.component c
+ WHERE c.isRequired;
+
+ INSERT INTO tmp.ticketProblems(ticketFk, componentLack)
+ SELECT tl.ticketFk, (COUNT(DISTINCT s.id) * vComponentCount > COUNT(c.id))
+ FROM tmp.ticketList tl
+ JOIN vn.sale s ON s.ticketFk = tl.ticketFk
+ LEFT JOIN vn.saleComponent sc ON sc.saleFk = s.id
+ LEFT JOIN vn.component c ON c.id = sc.componentFk AND c.isRequired
+ GROUP BY tl.ticketFk;
+
+ 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
+ ON DUPLICATE KEY UPDATE
+ isFreezed = c.isFreezed;
+
+ 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());
+
+ 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
+ JOIN vn.clientConfig cc
+ WHERE r.risk - cc.riskTolerance > c.credit + 10
+ AND a.isRiskFree = FALSE
+ ON DUPLICATE KEY UPDATE
+ risk = r.risk;
+
+ 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 AND tr.saleFk IS NOT NULL
+ ON DUPLICATE KEY UPDATE
+ hasTicketRequest = 1;
+
+ OPEN vCursor;
+
+ WHILE NOT vDone
+ DO
+ FETCH vCursor INTO vWarehouse, vDate;
+
+ CALL cache.available_refresh(vAvailableCache, FALSE, vWarehouse, vDate);
+
+ INSERT INTO tmp.ticketProblems(ticketFk, isAvailable)
+ SELECT tl.ticketFk, 0
+ FROM tmp.ticketList tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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.available av ON av.item_id = i.id
+ AND av.calc_id = vAvailableCache
+ WHERE date(t.shipped) = vDate
+ AND it.categoryFk != 6
+ 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;
+/*
+ INSERT INTO tmp.ticketProblems(ticketFk, itemShortage)
+ SELECT ticketFk, problem
+ FROM (
+ SELECT tl.ticketFk, CONCAT('F: ',GROUP_CONCAT(i.id, ' ', i.longName, ' ')) problem
+ FROM tmp.ticketList tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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 vn.itemShelvingStock_byWarehouse issw ON issw.itemFk = i.id AND issw.warehouseFk = t.warehouseFk
+ 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(issw.visible, 0)
+ AND s.quantity > 0
+ AND s.isPicked = FALSE
+ AND s.reserved = FALSE
+ AND it.categoryFk != 6
+ AND IF(vIsTodayRelative, TRUE, date(t.shipped) = vDate)
+ AND NOT i.generic
+ AND CURDATE() = vDate
+ AND t.warehouseFk = vWarehouse
+ GROUP BY tl.ticketFk LIMIT 1) sub
+ ON DUPLICATE KEY UPDATE
+ itemShortage = sub.problem;
+*/
+ INSERT INTO tmp.ticketProblems(ticketFk, itemDelay)
+ SELECT ticketFk, problem
+ FROM (
+ SELECT tl.ticketFk, GROUP_CONCAT('I: ',i.id, ' ', i.longName, ' ') problem
+ FROM tmp.ticketList tl
+ JOIN vn.ticket t ON t.id = tl.ticketFk
+ 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 vn.itemShelvingStock_byWarehouse issw ON issw.itemFk = i.id AND issw.warehouseFk = t.warehouseFk
+ WHERE s.quantity > IFNULL(issw.visible, 0)
+ AND s.quantity > 0
+ AND s.isPicked = FALSE
+ AND s.reserved = FALSE
+ AND it.categoryFk != 6
+ AND IF(vIsTodayRelative, TRUE, date(t.shipped) = vDate)
+ AND NOT i.generic
+ AND CURDATE() = vDate
+ AND t.warehouseFk = vWarehouse
+ GROUP BY tl.ticketFk LIMIT 1) sub
+ ON DUPLICATE KEY UPDATE
+ itemDelay = sub.problem;
+ END WHILE;
+
+ CLOSE vCursor;
+
+ INSERT INTO tmp.ticketProblems(ticketFk, isTaxDataChecked)
+ SELECT DISTINCT tl.ticketFk, FALSE
+ FROM tmp.ticketList tl
+ JOIN vn.client c ON c.id = tl.clientFk
+ WHERE c.isTaxDataChecked= FALSE
+ ON DUPLICATE KEY UPDATE
+ isTaxDataChecked = FALSE;
+
+ DROP TEMPORARY TABLE
+ tmp.clientGetDebt,
+ tmp.ticketList;
+
+END;;$$
+DELIMITER ;
diff --git a/db/changes/10320-monitors/00-ticket_getProblems.sql b/db/changes/10320-monitors/00-ticket_getProblems.sql
new file mode 100644
index 000000000..290a083df
--- /dev/null
+++ b/db/changes/10320-monitors/00-ticket_getProblems.sql
@@ -0,0 +1,36 @@
+DROP PROCEDURE IF EXISTS `vn`.`ticket_getProblems`;
+
+DELIMITER $$
+$$
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`ticket_getProblems`(IN vIsTodayRelative TINYINT(1))
+BEGIN
+/**
+ * Calcula los problemas para un conjunto de tickets.
+ * Agrupados por ticket
+ *
+ * @table tmp.sale_getProblems(ticketFk, clientFk, warehouseFk, shipped) Identificadores de los tickets a calcular
+ * @return tmp.ticket_problems
+ */
+ CALL sale_getProblems(vIsTodayRelative);
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticket_problems;
+ CREATE TEMPORARY TABLE tmp.ticket_problems
+ (INDEX (ticketFk))
+ ENGINE = MEMORY
+ SELECT
+ ticketFk,
+ MAX(p.isFreezed) AS isFreezed,
+ MAX(p.risk) AS risk,
+ MAX(p.hasTicketRequest) AS hasTicketRequest,
+ MIN(p.isAvailable) AS isAvailable,
+ MAX(p.itemShortage) AS itemShortage,
+ MIN(p.isTaxDataChecked) AS isTaxDataChecked,
+ MAX(p.hasComponentLack) AS hasComponentLack
+ FROM tmp.sale_problems p
+ GROUP BY ticketFk;
+
+ DROP TEMPORARY TABLE
+ tmp.sale_problems;
+END;;$$
+DELIMITER ;
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index b5bf46dd5..7b50935cc 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -565,18 +565,18 @@ export default {
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
- firstSaleId: 'vn-ticket-sale vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > span',
+ firstSaleId: 'vn-ticket-sale vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(5) > span',
firstSaleClaimIcon: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) vn-icon[icon="icon-claims"]',
firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img',
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-ticket-sale [ng-model="sale.quantity"]',
- firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(5)',
- firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
+ firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(6)',
+ firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(8) > span',
firstSalePriceInput: '.vn-popover.shown input[ng-model="$ctrl.field"]',
- firstSaleDiscount: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(8) > span',
+ firstSaleDiscount: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(9) > span',
firstSaleDiscountInput: '.vn-popover.shown [ng-model="$ctrl.field"]',
- firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(9)',
+ firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(10)',
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-fetched-tags section',
firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[ng-model="sale.checked"]',
@@ -584,8 +584,8 @@ export default {
secondSaleId: 'vn-ticket-sale:nth-child(2) vn-td-editable:nth-child(4) text > span',
secondSaleIdAutocomplete: 'vn-ticket-sale vn-tr:nth-child(2) vn-autocomplete[ng-model="sale.itemFk"]',
secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number',
- secondSaleQuantityCell: 'vn-ticket-sale > div > vn-card > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(5)',
- secondSaleConceptCell: 'vn-ticket-sale vn-tbody > :nth-child(2) > :nth-child(6)',
+ secondSaleQuantityCell: 'vn-ticket-sale > div > vn-card > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)',
+ secondSaleConceptCell: 'vn-ticket-sale vn-tbody > :nth-child(2) > :nth-child(7)',
secondSaleConceptInput: 'vn-ticket-sale vn-tbody > :nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield',
totalImport: 'vn-ticket-sale vn-one.taxes > p:nth-child(3) > strong',
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',
diff --git a/front/core/components/scroll-up/scroll-up.html b/front/core/components/scroll-up/scroll-up.html
index a3748acc6..e686bb077 100644
--- a/front/core/components/scroll-up/scroll-up.html
+++ b/front/core/components/scroll-up/scroll-up.html
@@ -1,6 +1,6 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/front/core/components/scroll-up/style.scss b/front/core/components/scroll-up/style.scss
index c6dae16ef..4b6fe70c1 100644
--- a/front/core/components/scroll-up/style.scss
+++ b/front/core/components/scroll-up/style.scss
@@ -1,10 +1,14 @@
@import "variables";
vn-scroll-up {
- right: 0;
+ left: 50%;
top: $topbar-height;
- margin: $float-spacing;
+ margin: 0;
display: none;
position: fixed;
- z-index: 10
+ z-index: 10;
+
+ vn-icon {
+ font-size: 60px
+ }
}
\ No newline at end of file
diff --git a/modules/monitor/back/methods/sales-monitor/salesFilter.js b/modules/monitor/back/methods/sales-monitor/salesFilter.js
index 726e5bfaf..ecbdc4895 100644
--- a/modules/monitor/back/methods/sales-monitor/salesFilter.js
+++ b/modules/monitor/back/methods/sales-monitor/salesFilter.js
@@ -247,10 +247,9 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt);
- stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticketGetProblems');
-
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmts.push(`
- CREATE TEMPORARY TABLE tmp.ticketGetProblems
+ CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
@@ -258,15 +257,12 @@ module.exports = Self => {
LEFT JOIN alertLevel al ON al.alertLevel = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
AND f.shipped >= CURDATE()`);
-
- stmts.push('CALL ticketGetProblems(FALSE)');
+ stmts.push('CALL ticket_getProblems(FALSE)');
stmt = new ParameterizedSQL(`
- SELECT
- f.*,
- tp.*
+ SELECT f.*, tp.*
FROM tmp.filter f
- LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.id`);
+ LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id`);
if (args.problems != undefined && (!args.from && !args.to))
throw new UserError('Choose a date range or days forward');
@@ -309,7 +305,7 @@ module.exports = Self => {
`DROP TEMPORARY TABLE
tmp.filter,
tmp.ticket,
- tmp.ticketGetProblems`);
+ tmp.ticket_problems`);
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
diff --git a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
index 0666544e7..54615b22b 100644
--- a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
+++ b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
@@ -23,7 +23,7 @@ describe('SalesMonitor salesFilter()', () => {
const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter);
- expect(result.length).toEqual(3);
+ expect(result.length).toEqual(4);
});
it('should return the tickets matching the problems on false', async() => {
diff --git a/modules/monitor/front/index/locale/es.yml b/modules/monitor/front/index/locale/es.yml
index 1f69de222..cb94d41a7 100644
--- a/modules/monitor/front/index/locale/es.yml
+++ b/modules/monitor/front/index/locale/es.yml
@@ -3,4 +3,5 @@ Clients on website: Clientes activos en la web
Recent order actions: Acciones recientes en pedidos
Search tickets: Buscar tickets
Delete selected elements: Eliminar los elementos seleccionados
-All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar?
\ No newline at end of file
+All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar?
+Component lack: Faltan componentes
\ No newline at end of file
diff --git a/modules/monitor/front/index/tickets/index.html b/modules/monitor/front/index/tickets/index.html
index e410be4e5..dffc1ee8d 100644
--- a/modules/monitor/front/index/tickets/index.html
+++ b/modules/monitor/front/index/tickets/index.html
@@ -30,7 +30,7 @@
-
+
@@ -67,7 +67,6 @@
ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
- vn-tooltip="Not available"
icon="icon-unavailable">
+
+
{{::ticket.packages}}
- {{::ticket.volume | number:1}}
+ {{::ticket.volume | number:2}}
{
it('should update the given sales of a ticket to reserved', async() => {
originalTicketSales = await app.models.Ticket.getSales(11);
- expect(originalTicketSales[0].reserved).toEqual(0);
- expect(originalTicketSales[1].reserved).toEqual(0);
+ expect(originalTicketSales[0].reserved).toEqual(false);
+ expect(originalTicketSales[1].reserved).toEqual(false);
const ticketId = 11;
const sales = [{id: 7}, {id: 8}];
@@ -46,7 +46,7 @@ describe('sale reserve()', () => {
const reservedTicketSales = await app.models.Ticket.getSales(ticketId);
- expect(reservedTicketSales[0].reserved).toEqual(1);
- expect(reservedTicketSales[1].reserved).toEqual(1);
+ expect(reservedTicketSales[0].reserved).toEqual(true);
+ expect(reservedTicketSales[1].reserved).toEqual(true);
});
});
diff --git a/modules/ticket/back/methods/ticket/filter.js b/modules/ticket/back/methods/ticket/filter.js
index 5298700f2..6826f55ec 100644
--- a/modules/ticket/back/methods/ticket/filter.js
+++ b/modules/ticket/back/methods/ticket/filter.js
@@ -247,10 +247,9 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt);
- stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticketGetProblems');
-
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmts.push(`
- CREATE TEMPORARY TABLE tmp.ticketGetProblems
+ CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
@@ -258,15 +257,12 @@ module.exports = Self => {
LEFT JOIN alertLevel al ON al.alertLevel = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
AND f.shipped >= CURDATE()`);
-
- stmts.push('CALL ticketGetProblems(FALSE)');
+ stmts.push('CALL ticket_getProblems(FALSE)');
stmt = new ParameterizedSQL(`
- SELECT
- f.*,
- tp.*
+ SELECT f.*, tp.*
FROM tmp.filter f
- LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.id`);
+ LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id`);
if (args.problems != undefined && (!args.from && !args.to))
throw new UserError('Choose a date range or days forward');
@@ -309,7 +305,7 @@ module.exports = Self => {
`DROP TEMPORARY TABLE
tmp.filter,
tmp.ticket,
- tmp.ticketGetProblems`);
+ tmp.ticket_problems`);
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
diff --git a/modules/ticket/back/methods/ticket/getSales.js b/modules/ticket/back/methods/ticket/getSales.js
index d522ffe98..7520e7270 100644
--- a/modules/ticket/back/methods/ticket/getSales.js
+++ b/modules/ticket/back/methods/ticket/getSales.js
@@ -1,12 +1,13 @@
+
module.exports = Self => {
Self.remoteMethod('getSales', {
description: 'New filter',
accessType: 'READ',
accepts: [{
- arg: 'ticketFk',
+ arg: 'id',
type: 'number',
required: true,
- description: 'ticket id',
+ description: 'The ticket id',
http: {source: 'path'}
}],
returns: {
@@ -14,60 +15,83 @@ module.exports = Self => {
root: true
},
http: {
- path: `/:ticketFk/getSales`,
+ path: `/:id/getSales`,
verb: 'get'
}
});
- Self.getSales = async ticketFk => {
- let query = `CALL vn.ticketGetVisibleAvailable(?)`;
- let [lines] = await Self.rawSql(query, [ticketFk]);
- let ids = [];
- let salesIds = [];
+ Self.getSales = async(id, options) => {
+ const models = Self.app.models;
- for (line of lines) {
- ids.push(line.itemFk);
- salesIds.push(line.id);
- }
+ let myOptions = {};
- let filter = {
- fields: [
- 'id',
- 'name',
- 'tag5',
- 'value5',
- 'tag6',
- 'value6',
- 'tag7',
- 'value7',
- 'tag8',
- 'value8',
- 'tag9',
- 'value9',
- 'tag10',
- 'value10'],
- where: {id: {inq: ids}}
- };
- let items = await Self.app.models.Item.find(filter);
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
- filter = {
+ const sales = await models.Sale.find({
+ include: {
+ relation: 'item',
+ scope: {
+ fields: [
+ 'id',
+ 'name',
+ 'tag5',
+ 'value5',
+ 'tag6',
+ 'value6',
+ 'tag7',
+ 'value7',
+ 'tag8',
+ 'value8',
+ 'tag9',
+ 'value9',
+ 'tag10',
+ 'value10'
+ ]
+ }
+ },
+ where: {ticketFk: id},
+ order: 'concept'
+ }, myOptions);
+
+ // Get items available
+ const query = `CALL ticketGetVisibleAvailable(?)`;
+ const [salesAvailable] = await Self.rawSql(query, [id], myOptions);
+
+ const itemAvailable = new Map();
+ for (let sale of salesAvailable)
+ itemAvailable.set(sale.itemFk, sale.available);
+
+ // Get claimed sales
+ const saleIds = sales.map(sale => sale.id);
+ const claims = await models.ClaimBeginning.find({
fields: ['claimFk', 'saleFk'],
- where: {saleFk: {inq: salesIds}},
- };
- let claims = await Self.app.models.ClaimBeginning.find(filter);
+ where: {saleFk: {inq: saleIds}},
+ }, myOptions);
- let map = {};
- for (item of items)
- map[item.id] = item;
+ const claimedSales = new Map();
+ for (let claim of claims)
+ claimedSales.set(claim.saleFk, claim);
- let claimMap = {};
- for (claim of claims)
- claimMap[claim.saleFk] = claim;
+ // Get problems
+ const problemsQuery = `CALL sale_getProblemsByTicket(?, FALSE)`;
+ const [problems] = await Self.rawSql(problemsQuery, [id], myOptions);
- for (line of lines) {
- line.item = map[line.itemFk];
- line.claim = claimMap[line.id];
+ const saleProblems = new Map();
+ for (let problem of problems)
+ saleProblems.set(problem.saleFk, problem);
+
+ for (let sale of sales) {
+ const problems = saleProblems.get(sale.id);
+ sale.available = itemAvailable.get(sale.itemFk);
+ sale.claim = claimedSales.get(sale.id);
+ if (problems) {
+ sale.isAvailable = problems.isAvailable;
+ sale.hasTicketRequest = problems.hasTicketRequest;
+ sale.hasComponentLack = problems.hasComponentLack;
+ }
}
- return lines;
+
+ return sales;
};
};
diff --git a/modules/ticket/back/methods/ticket/specs/filter.spec.js b/modules/ticket/back/methods/ticket/specs/filter.spec.js
index 0f4a1660e..c742de41d 100644
--- a/modules/ticket/back/methods/ticket/specs/filter.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/filter.spec.js
@@ -23,7 +23,7 @@ describe('ticket filter()', () => {
const filter = {};
const result = await app.models.Ticket.filter(ctx, filter);
- expect(result.length).toEqual(3);
+ expect(result.length).toEqual(4);
});
it('should return the tickets matching the problems on false', async() => {
diff --git a/modules/ticket/front/index/index.html b/modules/ticket/front/index/index.html
index 915e16a11..b86aaba9d 100644
--- a/modules/ticket/front/index/index.html
+++ b/modules/ticket/front/index/index.html
@@ -55,7 +55,6 @@
ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
- vn-tooltip="Not available"
icon="icon-unavailable">
+
+
{{::ticket.id}}
diff --git a/modules/ticket/front/index/locale/es.yml b/modules/ticket/front/index/locale/es.yml
index 1545c5e53..9ff8d1568 100644
--- a/modules/ticket/front/index/locale/es.yml
+++ b/modules/ticket/front/index/locale/es.yml
@@ -10,4 +10,5 @@ Exclude selection: Excluir selección
Remove filter: Quitar filtro por selección
Remove all filters: Eliminar todos los filtros
Copy value: Copiar valor
-No verified data: Sin datos comprobados
\ No newline at end of file
+No verified data: Sin datos comprobados
+Component lack: Faltan componentes
\ No newline at end of file
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index 5156206bb..0a5c8e841 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -59,6 +59,7 @@
+ Available
Id
Quantity
Item
@@ -82,14 +83,26 @@
+ vn-tooltip="Visible: {{::sale.visible || 0}}">
+ translate-attr="{title: 'Reserved'}">
+
+
+
+
@@ -98,6 +111,13 @@
zoom-image="{{::$root.imagePath('catalog', '1600x900', sale.itemFk)}}"
on-error-src/>
+
+
+ {{::sale.available}}
+
+
diff --git a/modules/ticket/front/summary/index.html b/modules/ticket/front/summary/index.html
index b2c322633..a2a17fb11 100644
--- a/modules/ticket/front/summary/index.html
+++ b/modules/ticket/front/summary/index.html
@@ -138,7 +138,28 @@
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimBeginning.claimFk}}">
-
+
+
+
+
+
+
+
+
- {{::sale.available}}
+
+ {{::sale.available}}
{{::sale.quantity}}
@@ -218,7 +242,11 @@
{{::service.quantity}}
{{::service.description}}
{{::service.price | currency: 'EUR':2}}
- {{::service.taxClass.description}}
+
+
+ {{::service.taxClass.description}}
+
+
{{::service.quantity * service.price | currency: 'EUR':2}}