2891 - Get ticket problems for every sale #629

Merged
carlosjr merged 7 commits from 2891-ticket_sale_problems into dev 2021-05-27 09:49:44 +00:00
20 changed files with 622 additions and 95 deletions

View File

@ -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 ;

View File

@ -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 ;

View File

@ -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 ;

View File

@ -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 ;

View File

@ -565,18 +565,18 @@ export default {
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input', 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', 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', 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"]', 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', 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', firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img',
firstSaleZoomedImage: 'body > div > div > img', firstSaleZoomedImage: 'body > div > div > img',
firstSaleQuantity: 'vn-ticket-sale [ng-model="sale.quantity"]', firstSaleQuantity: 'vn-ticket-sale [ng-model="sale.quantity"]',
firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(5)', 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(7) > span', 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"]', 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"]', 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)', 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', 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"]', 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', 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"]', 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', 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)', 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(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', 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', totalImport: 'vn-ticket-sale vn-one.taxes > p:nth-child(3) > strong',
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check', selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',

View File

@ -1,6 +1,6 @@
<vn-button <vn-icon-button
icon="keyboard_arrow_up" icon="keyboard_arrow_up"
ng-click="$ctrl.goUp()" ng-click="$ctrl.goUp()"
vn-tooltip="Go up" vn-tooltip="Go up"
class="round"> class="round">
</vn-button> </vn-icon-button>

View File

@ -1,10 +1,14 @@
@import "variables"; @import "variables";
vn-scroll-up { vn-scroll-up {
right: 0; left: 50%;
top: $topbar-height; top: $topbar-height;
margin: $float-spacing; margin: 0;
display: none; display: none;
position: fixed; position: fixed;
z-index: 10 z-index: 10;
vn-icon {
font-size: 60px
}
} }

View File

@ -247,10 +247,9 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt); stmts.push(stmt);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticketGetProblems'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmts.push(` stmts.push(`
CREATE TEMPORARY TABLE tmp.ticketGetProblems CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk)) (INDEX (ticketFk))
ENGINE = MEMORY ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped 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 LEFT JOIN alertLevel al ON al.alertLevel = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL) WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
AND f.shipped >= CURDATE()`); AND f.shipped >= CURDATE()`);
stmts.push('CALL ticket_getProblems(FALSE)');
stmts.push('CALL ticketGetProblems(FALSE)');
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`
SELECT SELECT f.*, tp.*
f.*,
tp.*
FROM tmp.filter f 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)) if (args.problems != undefined && (!args.from && !args.to))
throw new UserError('Choose a date range or days forward'); throw new UserError('Choose a date range or days forward');
@ -309,7 +305,7 @@ module.exports = Self => {
`DROP TEMPORARY TABLE `DROP TEMPORARY TABLE
tmp.filter, tmp.filter,
tmp.ticket, tmp.ticket,
tmp.ticketGetProblems`); tmp.ticket_problems`);
let sql = ParameterizedSQL.join(stmts, ';'); let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql); let result = await conn.executeStmt(sql);

View File

@ -23,7 +23,7 @@ describe('SalesMonitor salesFilter()', () => {
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, 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() => { it('should return the tickets matching the problems on false', async() => {

View File

@ -3,4 +3,5 @@ Clients on website: Clientes activos en la web
Recent order actions: Acciones recientes en pedidos Recent order actions: Acciones recientes en pedidos
Search tickets: Buscar tickets Search tickets: Buscar tickets
Delete selected elements: Eliminar los elementos seleccionados 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? 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

View File

@ -30,7 +30,7 @@
</vn-none> </vn-none>
</vn-horizontal> </vn-horizontal>
<vn-card> <vn-card>
<vn-table model="model" class="scrollable"> <vn-table model="model">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th class="icon-field"></vn-th> <vn-th class="icon-field"></vn-th>
@ -67,7 +67,6 @@
ng-show="::ticket.isAvailable === 0" ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not available'}"
class="bright" class="bright"
vn-tooltip="Not available"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
@ -82,6 +81,12 @@
class="bright" class="bright"
icon="icon-risk"> icon="icon-risk">
</vn-icon> </vn-icon>
<vn-icon
ng-show="::ticket.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon>
</vn-td> </vn-td>
<vn-td expand> <vn-td expand>
<span <span

View File

@ -74,7 +74,7 @@
</span> </span>
</vn-td> </vn-td>
<vn-td shrink>{{::ticket.packages}}</vn-td> <vn-td shrink>{{::ticket.packages}}</vn-td>
<vn-td shrink>{{::ticket.volume | number:1}}</vn-td> <vn-td shrink>{{::ticket.volume | number:2}}</vn-td>
<vn-td number> <vn-td number>
<span <span
ng-click="ticketDescriptor.show($event, ticket.id)" ng-click="ticketDescriptor.show($event, ticket.id)"

View File

@ -35,8 +35,8 @@ describe('sale reserve()', () => {
it('should update the given sales of a ticket to reserved', async() => { it('should update the given sales of a ticket to reserved', async() => {
originalTicketSales = await app.models.Ticket.getSales(11); originalTicketSales = await app.models.Ticket.getSales(11);
expect(originalTicketSales[0].reserved).toEqual(0); expect(originalTicketSales[0].reserved).toEqual(false);
expect(originalTicketSales[1].reserved).toEqual(0); expect(originalTicketSales[1].reserved).toEqual(false);
const ticketId = 11; const ticketId = 11;
const sales = [{id: 7}, {id: 8}]; const sales = [{id: 7}, {id: 8}];
@ -46,7 +46,7 @@ describe('sale reserve()', () => {
const reservedTicketSales = await app.models.Ticket.getSales(ticketId); const reservedTicketSales = await app.models.Ticket.getSales(ticketId);
expect(reservedTicketSales[0].reserved).toEqual(1); expect(reservedTicketSales[0].reserved).toEqual(true);
expect(reservedTicketSales[1].reserved).toEqual(1); expect(reservedTicketSales[1].reserved).toEqual(true);
}); });
}); });

View File

@ -247,10 +247,9 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt); stmts.push(stmt);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticketGetProblems'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmts.push(` stmts.push(`
CREATE TEMPORARY TABLE tmp.ticketGetProblems CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk)) (INDEX (ticketFk))
ENGINE = MEMORY ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped 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 LEFT JOIN alertLevel al ON al.alertLevel = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL) WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
AND f.shipped >= CURDATE()`); AND f.shipped >= CURDATE()`);
stmts.push('CALL ticket_getProblems(FALSE)');
stmts.push('CALL ticketGetProblems(FALSE)');
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`
SELECT SELECT f.*, tp.*
f.*,
tp.*
FROM tmp.filter f 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)) if (args.problems != undefined && (!args.from && !args.to))
throw new UserError('Choose a date range or days forward'); throw new UserError('Choose a date range or days forward');
@ -309,7 +305,7 @@ module.exports = Self => {
`DROP TEMPORARY TABLE `DROP TEMPORARY TABLE
tmp.filter, tmp.filter,
tmp.ticket, tmp.ticket,
tmp.ticketGetProblems`); tmp.ticket_problems`);
let sql = ParameterizedSQL.join(stmts, ';'); let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql); let result = await conn.executeStmt(sql);

View File

@ -1,12 +1,13 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getSales', { Self.remoteMethod('getSales', {
description: 'New filter', description: 'New filter',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
arg: 'ticketFk', arg: 'id',
type: 'number', type: 'number',
required: true, required: true,
description: 'ticket id', description: 'The ticket id',
http: {source: 'path'} http: {source: 'path'}
}], }],
returns: { returns: {
@ -14,60 +15,83 @@ module.exports = Self => {
root: true root: true
}, },
http: { http: {
path: `/:ticketFk/getSales`, path: `/:id/getSales`,
verb: 'get' verb: 'get'
} }
}); });
Self.getSales = async ticketFk => { Self.getSales = async(id, options) => {
let query = `CALL vn.ticketGetVisibleAvailable(?)`; const models = Self.app.models;
let [lines] = await Self.rawSql(query, [ticketFk]);
let ids = [];
let salesIds = [];
for (line of lines) { let myOptions = {};
ids.push(line.itemFk);
salesIds.push(line.id);
}
let filter = { if (typeof options == 'object')
fields: [ Object.assign(myOptions, options);
'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);
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'], fields: ['claimFk', 'saleFk'],
where: {saleFk: {inq: salesIds}}, where: {saleFk: {inq: saleIds}},
}; }, myOptions);
let claims = await Self.app.models.ClaimBeginning.find(filter);
let map = {}; const claimedSales = new Map();
for (item of items) for (let claim of claims)
map[item.id] = item; claimedSales.set(claim.saleFk, claim);
let claimMap = {}; // Get problems
for (claim of claims) const problemsQuery = `CALL sale_getProblemsByTicket(?, FALSE)`;
claimMap[claim.saleFk] = claim; const [problems] = await Self.rawSql(problemsQuery, [id], myOptions);
for (line of lines) { const saleProblems = new Map();
line.item = map[line.itemFk]; for (let problem of problems)
line.claim = claimMap[line.id]; 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;
}; };
}; };

View File

@ -23,7 +23,7 @@ describe('ticket filter()', () => {
const filter = {}; const filter = {};
const result = await app.models.Ticket.filter(ctx, 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() => { it('should return the tickets matching the problems on false', async() => {

View File

@ -55,7 +55,6 @@
ng-show="::ticket.isAvailable === 0" ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not available'}"
class="bright" class="bright"
vn-tooltip="Not available"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
@ -70,6 +69,12 @@
class="bright" class="bright"
icon="icon-risk"> icon="icon-risk">
</vn-icon> </vn-icon>
<vn-icon
ng-show="::ticket.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon>
</vn-td> </vn-td>
<vn-td shrink>{{::ticket.id}}</vn-td> <vn-td shrink>{{::ticket.id}}</vn-td>
<vn-td class="expendable"> <vn-td class="expendable">

View File

@ -10,4 +10,5 @@ Exclude selection: Excluir selección
Remove filter: Quitar filtro por selección Remove filter: Quitar filtro por selección
Remove all filters: Eliminar todos los filtros Remove all filters: Eliminar todos los filtros
Copy value: Copiar valor Copy value: Copiar valor
No verified data: Sin datos comprobados No verified data: Sin datos comprobados
Component lack: Faltan componentes

View File

@ -59,6 +59,7 @@
</vn-th> </vn-th>
<vn-th shrink></vn-th> <vn-th shrink></vn-th>
<vn-th shrink></vn-th> <vn-th shrink></vn-th>
<vn-th number shrink>Available</vn-th>
<vn-th shrink id="ticketId">Id</vn-th> <vn-th shrink id="ticketId">Id</vn-th>
<vn-th shrink>Quantity</vn-th> <vn-th shrink>Quantity</vn-th>
<vn-th>Item</vn-th> <vn-th>Item</vn-th>
@ -82,14 +83,26 @@
</vn-icon> </vn-icon>
</a> </a>
<vn-icon <vn-icon
ng-show="::(sale.visible < 0 || sale.available < 0)" ng-show="::(sale.visible < 0)"
color-main color-main
icon="warning" icon="warning"
vn-tooltip="Visible: {{::sale.visible || 0}} <br> {{::$ctrl.$t('Available')}}: {{::sale.available || 0}}"> vn-tooltip="Visible: {{::sale.visible || 0}}">
</vn-icon> </vn-icon>
<vn-icon ng-show="sale.reserved" <vn-icon ng-show="sale.reserved"
icon="icon-reserve" icon="icon-reserve"
vn-tooltip="{{::$ctrl.$t('Reserved')}}"> translate-attr="{title: 'Reserved'}">
</vn-icon>
<vn-icon
ng-show="::sale.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
icon="icon-unavailable">
</vn-icon>
<vn-icon
ng-show="::sale.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon> </vn-icon>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
@ -98,6 +111,13 @@
zoom-image="{{::$root.imagePath('catalog', '1600x900', sale.itemFk)}}" zoom-image="{{::$root.imagePath('catalog', '1600x900', sale.itemFk)}}"
on-error-src/> on-error-src/>
</vn-td> </vn-td>
<vn-td number shrink>
<vn-chip
class="transparent"
ng-class="{'message': sale.available < 0}">
{{::sale.available}}
</vn-chip>
</vn-td>
<vn-td shrink> <vn-td shrink>
<span class="link" ng-if="sale.id" <span class="link" ng-if="sale.id"
ng-click="itemDescriptor.show($event, sale.itemFk, sale.id)"> ng-click="itemDescriptor.show($event, sale.itemFk, sale.id)">

View File

@ -138,7 +138,28 @@
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimBeginning.claimFk}}"> vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimBeginning.claimFk}}">
</vn-icon> </vn-icon>
</a> </a>
<vn-icon ng-show="sale.reserved" icon="icon-reserva"></vn-icon> <vn-icon
ng-show="::(sale.visible < 0)"
color-main
icon="warning"
vn-tooltip="Visible: {{::sale.visible || 0}}">
</vn-icon>
<vn-icon ng-show="sale.reserved"
icon="icon-reserve"
translate-attr="{title: 'Reserved'}">
</vn-icon>
<vn-icon
ng-show="::sale.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
icon="icon-unavailable">
</vn-icon>
<vn-icon
ng-show="::sale.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon>
</vn-td> </vn-td>
<vn-td number shrink> <vn-td number shrink>
<span <span
@ -148,7 +169,10 @@
</span> </span>
</vn-td> </vn-td>
<vn-td number shrink> <vn-td number shrink>
<vn-chip class="transparent" ng-class="{'message': sale.available < 0}">{{::sale.available}} <vn-chip
class="transparent"
ng-class="{'message': sale.available < 0}">
{{::sale.available}}
</vn-chip> </vn-chip>
</vn-td> </vn-td>
<vn-td number shrink>{{::sale.quantity}}</vn-td> <vn-td number shrink>{{::sale.quantity}}</vn-td>
@ -218,7 +242,11 @@
<vn-td number shrink>{{::service.quantity}}</vn-td> <vn-td number shrink>{{::service.quantity}}</vn-td>
<vn-td expand>{{::service.description}}</vn-td> <vn-td expand>{{::service.description}}</vn-td>
<vn-td number shrink>{{::service.price | currency: 'EUR':2}}</vn-td> <vn-td number shrink>{{::service.price | currency: 'EUR':2}}</vn-td>
<vn-td class="tax-class"><span title="{{::service.taxClass.description}}">{{::service.taxClass.description}}</span></vn-td> <vn-td class="tax-class">
<span title="{{::service.taxClass.description}}">
{{::service.taxClass.description}}
</span>
</vn-td>
<vn-td number>{{::service.quantity * service.price | currency: 'EUR':2}}</vn-td> <vn-td number>{{::service.quantity * service.price | currency: 'EUR':2}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>