8032-devToTest_2440 #3009

Merged
alexm merged 262 commits from 8032-devToTest_2440 into test 2024-09-24 09:34:49 +00:00
53 changed files with 422 additions and 287 deletions
Showing only changes of commit 504e2f0ed2 - Show all commits

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` EVENT `util`.`log_clean`
ON SCHEDULE EVERY 1 DAY
STARTS '2024-07-09 00:30:00.000'
ON COMPLETION PRESERVE
ENABLE
DO CALL util.log_clean$$
DELIMITER ;

View File

@ -0,0 +1,54 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`log_clean`()
BEGIN
/**
* Hace limpieza de los datos de las tablas log,
* dejando únicamente los días de retención configurados.
*/
DECLARE vSchemaName VARCHAR(65);
DECLARE vSchemaNameQuoted VARCHAR(65);
DECLARE vTableName VARCHAR(65);
DECLARE vTableNameQuoted VARCHAR(65);
DECLARE vRetentionDays INT;
DECLARE vStarted DATETIME;
DECLARE vDated DATE;
DECLARE vDone BOOL;
DECLARE vQueue CURSOR FOR
SELECT schemaName, tableName, retentionDays
FROM logCleanMultiConfig
ORDER BY `order`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
OPEN vQueue;
l: LOOP
SET vDone = FALSE;
FETCH vQueue INTO vSchemaName, vTableName, vRetentionDays;
IF vDone THEN
LEAVE l;
END IF;
IF vRetentionDays THEN
SET vStarted = VN_NOW();
SET vSchemaNameQuoted = quoteIdentifier(vSchemaName);
SET vTableNameQuoted = quoteIdentifier(vTableName);
SET vDated = VN_CURDATE() - INTERVAL vRetentionDays DAY;
EXECUTE IMMEDIATE CONCAT(
'DELETE FROM ', vSchemaNameQuoted,
'.', vTableNameQuoted,
" WHERE creationDate < '", vDated, "'"
);
UPDATE logCleanMultiConfig
SET `started` = vStarted,
`finished` = VN_NOW()
WHERE schemaName = vSchemaName
AND tableName = vTableName;
END IF;
END LOOP;
CLOSE vQueue;
END$$
DELIMITER ;

View File

@ -30,6 +30,8 @@ BEGIN
FROM operator FROM operator
WHERE workerFk = account.myUser_getId(); WHERE workerFk = account.myUser_getId();
CALL util.debugAdd('itemShelvingSale_addBySectorCollection',CONCAT(vSectorCollectionFk,' - ', account.myUser_getId()));
OPEN vSales; OPEN vSales;
l: LOOP l: LOOP
SET vDone = FALSE; SET vDone = FALSE;

View File

@ -51,7 +51,6 @@ BEGIN
hasTicketRequest, hasTicketRequest,
isTaxDataChecked, isTaxDataChecked,
hasComponentLack, hasComponentLack,
hasRounding,
isTooLittle) isTooLittle)
SELECT sgp.ticketFk, SELECT sgp.ticketFk,
s.id, s.id,
@ -62,10 +61,6 @@ BEGIN
IF(FIND_IN_SET('hasTicketRequest', t.problem), TRUE, FALSE) hasTicketRequest, IF(FIND_IN_SET('hasTicketRequest', t.problem), TRUE, FALSE) hasTicketRequest,
IF(FIND_IN_SET('isTaxDataChecked', t.problem), FALSE, TRUE) isTaxDataChecked, IF(FIND_IN_SET('isTaxDataChecked', t.problem), FALSE, TRUE) isTaxDataChecked,
IF(FIND_IN_SET('hasComponentLack', s.problem), TRUE, FALSE) hasComponentLack, IF(FIND_IN_SET('hasComponentLack', s.problem), TRUE, FALSE) hasComponentLack,
IF(FIND_IN_SET('hasRounding', s.problem),
LEFT(GROUP_CONCAT('RE: ', i.id, ' ', IFNULL(i.longName,'') SEPARATOR ', '), 250),
NULL
) hasRounding,
IF(FIND_IN_SET('isTooLittle', t.problem) IF(FIND_IN_SET('isTooLittle', t.problem)
AND util.VN_NOW() < (util.VN_CURDATE() + INTERVAL HOUR(zc.`hour`) HOUR) + INTERVAL MINUTE(zc.`hour`) MINUTE, AND util.VN_NOW() < (util.VN_CURDATE() + INTERVAL HOUR(zc.`hour`) HOUR) + INTERVAL MINUTE(zc.`hour`) MINUTE,
TRUE, FALSE) isTooLittle TRUE, FALSE) isTooLittle
@ -208,24 +203,31 @@ BEGIN
GROUP BY sgp.ticketFk GROUP BY sgp.ticketFk
) sub ) sub
ON DUPLICATE KEY UPDATE itemDelay = sub.problem, saleFk = sub.saleFk; ON DUPLICATE KEY UPDATE itemDelay = sub.problem, saleFk = sub.saleFk;
-- Redondeo: cantidad incorrecta con respecto al grouping
CALL buy_getUltimate(NULL, vWarehouseFk, vDate);
INSERT INTO tmp.sale_problems(ticketFk, hasRounding, saleFk)
SELECT ticketFk, problem, saleFk
FROM (
SELECT sgp.ticketFk,
s.id saleFk,
LEFT(GROUP_CONCAT('RE: ',i.id, ' ', IFNULL(i.longName,'') SEPARATOR ', '), 250) problem
FROM tmp.sale_getProblems sgp
JOIN ticket t ON t.id = sgp.ticketFk
AND t.warehouseFk = vWarehouseFk
JOIN sale s ON s.ticketFk = sgp.ticketFk
JOIN item i ON i.id = s.itemFk
JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
JOIN buy b ON b.id = bu.buyFk
WHERE MOD(s.quantity, b.`grouping`)
GROUP BY sgp.ticketFk
)sub
ON DUPLICATE KEY UPDATE hasRounding = sub.problem, saleFk = sub.saleFk;
DROP TEMPORARY TABLE tmp.buyUltimate;
END LOOP; END LOOP;
CLOSE vCursor; CLOSE vCursor;
INSERT INTO tmp.sale_problems(ticketFk, hasRounding, saleFk)
SELECT ticketFk, problem, saleFk
FROM (
SELECT sgp.ticketFk,
s.id saleFk,
LEFT(GROUP_CONCAT('RE: ', i.id, ' ', IFNULL(i.longName,'') SEPARATOR ', '), 250) problem
FROM tmp.sale_getProblems sgp
JOIN ticket t ON t.id = sgp.ticketFk
JOIN sale s ON s.ticketFk = sgp.ticketFk
JOIN item i ON i.id = s.itemFk
WHERE FIND_IN_SET('hasRounding', s.problem)
GROUP BY sgp.ticketFk
) sub
ON DUPLICATE KEY UPDATE hasRounding = sub.problem, saleFk = sub.saleFk;
DROP TEMPORARY TABLE tItemShelvingStock_byWarehouse; DROP TEMPORARY TABLE tItemShelvingStock_byWarehouse;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -1,37 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`sale_setProblemRounding`(
vSelf INT
)
BEGIN
/**
* Update the rounding problem for a sales line
* @param vSelf Id sale
*/
DECLARE vItemFk INT;
DECLARE vWarehouseFk INT;
DECLARE vShipped DATE;
DECLARE vQuantity INT;
DECLARE vIsProblemCalcNeeded BOOL;
SELECT s.itemFk, t.warehouseFk, t.shipped, s.quantity, ticket_isProblemCalcNeeded(t.id)
INTO vItemFk, vWarehouseFk, vShipped, vQuantity, vIsProblemCalcNeeded
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
WHERE s.id = vSelf;
CALL buy_getUltimate(vItemFk, vWarehouseFk, vShipped);
CREATE OR REPLACE TEMPORARY TABLE tmp.sale
SELECT vSelf saleFk,
MOD(vQuantity, b.`grouping`) hasProblem,
vIsProblemCalcNeeded isProblemCalcNeeded
FROM tmp.buyUltimate bu
JOIN buy b ON b.id = bu.buyFk
WHERE bu.itemFk = vItemFk;
CALL sale_setProblem('hasRounding');
DROP TEMPORARY TABLE tmp.sale;
DROP TEMPORARY TABLE tmp.buyUltimate;
END$$
DELIMITER ;

View File

@ -1,75 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_setProblemRoundingByBuy`(
vBuyFk INT
)
BEGIN
/**
* Update rounding problem for all sales related to a buy.
*
* @param vBuyFk Buy id
*/
DECLARE vItemFk INT;
DECLARE vWarehouseFk INT;
DECLARE vMaxDated DATE;
DECLARE vMinDated DATE;
DECLARE vLanding DATE;
DECLARE vLastBuy INT;
DECLARE vCurrentBuy INT;
DECLARE vGrouping INT;
SELECT b.itemFk, t.warehouseInFk
INTO vItemFk, vWarehouseFk
FROM buy b
JOIN entry e ON e.id = b.entryFk
JOIN travel t ON t.id = e.travelFk
WHERE b.id = vBuyFk;
IF vItemFk AND vWarehouseFk THEN
SELECT DATE(MAX(t.shipped)) + INTERVAL 1 DAY, DATE(MIN(t.shipped))
INTO vMaxDated, vMinDated
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
WHERE t.shipped >= util.VN_CURDATE()
AND s.itemFk = vItemFk
AND s.quantity > 0;
CALL buy_getUltimate(vItemFk, vWarehouseFk, vMinDated);
SELECT bu.buyFk, b.grouping INTO vLastBuy, vGrouping
FROM tmp.buyUltimate bu
JOIN buy b ON b.id = bu.buyFk;
DROP TEMPORARY TABLE tmp.buyUltimate;
SET vLanding = vMaxDated;
WHILE vCurrentBuy <> vLastBuy OR vLanding > vMinDated DO
SET vMaxDated = vLanding - INTERVAL 1 DAY;
CALL buy_getUltimate(vItemFk, vWarehouseFk, vMaxDated);
SELECT buyFk, landing
INTO vCurrentBuy, vLanding
FROM tmp.buyUltimate;
DROP TEMPORARY TABLE tmp.buyUltimate;
END WHILE;
CREATE OR REPLACE TEMPORARY TABLE tmp.sale
(INDEX(saleFk, isProblemCalcNeeded))
ENGINE = MEMORY
SELECT s.id saleFk,
MOD(s.quantity, vGrouping) hasProblem,
ticket_isProblemCalcNeeded(t.id) isProblemCalcNeeded
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
WHERE s.itemFk = vItemFk
AND s.quantity > 0
AND t.shipped BETWEEN vMinDated AND util.dayEnd(vMaxDated);
CALL sale_setProblem('hasRounding');
DROP TEMPORARY TABLE tmp.sale;
END IF;
END$$
DELIMITER ;

View File

@ -19,8 +19,7 @@ BEGIN
CREATE OR REPLACE TEMPORARY TABLE tmp.filter CREATE OR REPLACE TEMPORARY TABLE tmp.filter
(INDEX (id)) (INDEX (id))
SELECT SELECT origin.ticketFk futureId,
origin.ticketFk futureId,
dest.ticketFk id, dest.ticketFk id,
dest.state, dest.state,
origin.futureState, origin.futureState,
@ -51,48 +50,48 @@ BEGIN
origin.warehouseFk futureWarehouseFk, origin.warehouseFk futureWarehouseFk,
origin.companyFk futureCompanyFk, origin.companyFk futureCompanyFk,
IFNULL(dest.nickname, origin.nickname) nickname, IFNULL(dest.nickname, origin.nickname) nickname,
dest.landed dest.landed,
dest.preparation
FROM ( FROM (
SELECT SELECT s.ticketFk,
s.ticketFk, c.salesPersonFk workerFk,
c.salesPersonFk workerFk, t.shipped,
t.shipped, t.totalWithVat,
t.totalWithVat, st.name futureState,
st.name futureState, am.name futureAgency,
am.name futureAgency, count(s.id) futureLines,
count(s.id) futureLines, GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt, CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters, SUM(s.quantity <= (IFNULL(il.stock,0) + IFNULL(im.amount, 0))) hasStock,
SUM(s.quantity <= (IFNULL(il.stock,0) + IFNULL(im.amount, 0))) hasStock, z.id futureZoneFk,
z.id futureZoneFk, z.name futureZoneName,
z.name futureZoneName, st.classColor,
st.classColor, t.clientFk,
t.clientFk, t.nickname,
t.nickname, t.addressFk,
t.addressFk, t.warehouseFk,
t.warehouseFk, t.companyFk,
t.companyFk, t.agencyModeFk
t.agencyModeFk FROM ticket t
FROM ticket t JOIN client c ON c.id = t.clientFk
JOIN client c ON c.id = t.clientFk JOIN sale s ON s.ticketFk = t.id
JOIN sale s ON s.ticketFk = t.id JOIN saleVolume sv ON sv.saleFk = s.id
JOIN saleVolume sv ON sv.saleFk = s.id JOIN item i ON i.id = s.itemFk
JOIN item i ON i.id = s.itemFk JOIN ticketState ts ON ts.ticketFk = t.id
JOIN ticketState ts ON ts.ticketFk = t.id JOIN `state` st ON st.id = ts.stateFk
JOIN state st ON st.id = ts.stateFk JOIN agencyMode am ON t.agencyModeFk = am.id
JOIN agencyMode am ON t.agencyModeFk = am.id JOIN `zone` z ON t.zoneFk = z.id
JOIN zone z ON t.zoneFk = z.id LEFT JOIN zoneClosure zc ON zc.zoneFk = t.zoneFk
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN tmp.itemMinacum im ON im.itemFk = i.id LEFT JOIN tmp.itemMinacum im ON im.itemFk = i.id
AND im.warehouseFk = vWarehouseFk AND im.warehouseFk = vWarehouseFk
LEFT JOIN tmp.itemList il ON il.itemFk = i.id LEFT JOIN tmp.itemList il ON il.itemFk = i.id
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture) WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk AND t.warehouseFk = vWarehouseFk
GROUP BY t.id GROUP BY t.id
) origin ) origin
LEFT JOIN ( LEFT JOIN (
SELECT SELECT t.id ticketFk,
t.id ticketFk,
st.name state, st.name state,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt, GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
t.shipped, t.shipped,
@ -108,18 +107,25 @@ BEGIN
t.warehouseFk, t.warehouseFk,
t.companyFk, t.companyFk,
t.landed, t.landed,
t.agencyModeFk t.agencyModeFk,
SEC_TO_TIME(
COALESCE(HOUR(t.shipped), HOUR(zc.hour), HOUR(z.hour)) * 3600 +
COALESCE(MINUTE(t.shipped), MINUTE(zc.hour), MINUTE(z.hour)) * 60
) preparation
FROM ticket t FROM ticket t
JOIN sale s ON s.ticketFk = t.id JOIN sale s ON s.ticketFk = t.id
JOIN saleVolume sv ON sv.saleFk = s.id JOIN saleVolume sv ON sv.saleFk = s.id
JOIN item i ON i.id = s.itemFk JOIN item i ON i.id = s.itemFk
JOIN ticketState ts ON ts.ticketFk = t.id JOIN ticketState ts ON ts.ticketFk = t.id
JOIN state st ON st.id = ts.stateFk JOIN `state` st ON st.id = ts.stateFk
JOIN agencyMode am ON t.agencyModeFk = am.id JOIN agencyMode am ON t.agencyModeFk = am.id
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN `zone` z ON z.id = t.zoneFk
LEFT JOIN zoneClosure zc ON zc.zoneFk = t.zoneFk
JOIN ticketCanAdvanceConfig tc
WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance) WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
AND t.warehouseFk = vWarehouseFk AND t.warehouseFk = vWarehouseFk
AND st.order <= 5 AND st.order <= tc.destinationOrder
GROUP BY t.id GROUP BY t.id
) dest ON dest.addressFk = origin.addressFk ) dest ON dest.addressFk = origin.addressFk
WHERE origin.hasStock; WHERE origin.hasStock;

View File

@ -21,6 +21,7 @@ BEGIN
t.clientFk, t.clientFk,
t.warehouseFk, t.warehouseFk,
ts.alertLevel, ts.alertLevel,
sub2.alertLevel futureAlertLevel,
t.shipped, t.shipped,
t.totalWithVat, t.totalWithVat,
sub2.shipped futureShipped, sub2.shipped futureShipped,
@ -47,6 +48,7 @@ BEGIN
t.addressFk, t.addressFk,
t.id, t.id,
t.shipped, t.shipped,
ts.alertLevel,
st.name state, st.name state,
st.code, st.code,
st.classColor, st.classColor,

View File

@ -36,7 +36,7 @@ BEGIN
UPDATE tmp.ticket_problems UPDATE tmp.ticket_problems
SET totalProblems = ( SET totalProblems = (
(isFreezed) + (isFreezed) +
(hasRisk) + (hasHighRisk) +
(hasTicketRequest) + (hasTicketRequest) +
(!isTaxDataChecked) + (!isTaxDataChecked) +
(hasComponentLack) + (hasComponentLack) +

View File

@ -1,37 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`ticket_setProblemRounding`(
vSelf INT
)
BEGIN
/**
* Update the rounding problem for the sales lines of a ticket
*
* @param vSelf Id de ticket
*/
DECLARE vWarehouseFk INT;
DECLARE vDated DATE;
SELECT warehouseFk, shipped
INTO vWarehouseFk, vDated
FROM ticket
WHERE id = vSelf;
CALL buy_getUltimate(NULL, vWarehouseFk, vDated);
CREATE OR REPLACE TEMPORARY TABLE tmp.sale
(INDEX(saleFk, isProblemCalcNeeded))
SELECT s.id saleFk ,
MOD(s.quantity, b.`grouping`) hasProblem,
ticket_isProblemCalcNeeded(t.id) isProblemCalcNeeded
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
JOIN buy b ON b.id = bu.buyFk
WHERE t.id = vSelf;
CALL sale_setProblem('hasRounding');
DROP TEMPORARY TABLE tmp.sale;
DROP TEMPORARY TABLE tmp.buyUltimate;
END$$
DELIMITER ;

View File

@ -0,0 +1,13 @@
CREATE OR REPLACE TABLE `util`.`logCleanMultiConfig` (
`schemaName` varchar(64) NOT NULL,
`tableName` varchar(64) NOT NULL,
`retentionDays` int(11) DEFAULT NULL,
`order` int(11) DEFAULT NULL,
`started` datetime DEFAULT NULL,
`finished` datetime DEFAULT NULL,
PRIMARY KEY (`schemaName`,`tableName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `util`.`logCleanMultiConfig` (`schemaName`, `tableName`)
SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.`COLUMNS`
WHERE COLUMN_NAME IN ('newInstance', 'newInstance');

View File

@ -0,0 +1 @@
CREATE INDEX userLog_creationDate_IDX USING BTREE ON account.userLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX roleLog_creationDate_IDX USING BTREE ON account.roleLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX ACLLog_creationDate_IDX USING BTREE ON salix.ACLLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX supplierLog_creationDate_IDX USING BTREE ON vn.supplierLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX deviceProductionLog_creationDate_IDX USING BTREE ON vn.deviceProductionLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX routeLog_creationDate_IDX USING BTREE ON vn.routeLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX itemLog_creationDate_IDX USING BTREE ON vn.itemLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX userLog_creationDate_IDX USING BTREE ON vn.userLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX invoiceInLog_creationDate_IDX USING BTREE ON vn.invoiceInLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX zoneLog_creationDate_IDX USING BTREE ON vn.zoneLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX clientLog_creationDate_IDX USING BTREE ON vn.clientLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX workerLog_creationDate_IDX USING BTREE ON vn.workerLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX rateLog_creationDate_IDX USING BTREE ON vn.rateLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX claimLog_creationDate_IDX USING BTREE ON vn.claimLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX packingSiteDeviceLog_creationDate_IDX USING BTREE ON vn.packingSiteDeviceLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX travelLog_creationDate_IDX USING BTREE ON vn.travelLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX shelvingLog_creationDate_IDX USING BTREE ON vn.shelvingLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX productionConfigLog_creationDate_IDX USING BTREE ON vn.productionConfigLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX entryLog_creationDate_IDX USING BTREE ON vn.entryLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX agencyLog_creationDate_IDX USING BTREE ON vn.agencyLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX parkingLog_creationDate_IDX USING BTREE ON vn.parkingLog (creationDate DESC);

View File

@ -0,0 +1 @@
CREATE INDEX bufferLog_creationDate_IDX USING BTREE ON srt.bufferLog (creationDate DESC);

View File

@ -0,0 +1,9 @@
-- Place your SQL code here
INSERT INTO salix.ACL
SET model = 'Ticket',
property = 'setWeight',
accessType = 'WRITE',
permission = 'ALLOW',
principalType = 'ROLE',
principalId = 'salesPerson';

View File

@ -0,0 +1 @@
CREATE INDEX saleGroupLog_creationDate_IDX USING BTREE ON vn.saleGroupLog (creationDate DESC);

View File

@ -0,0 +1,9 @@
-- Place your SQL code here
CREATE TABLE IF NOT EXISTS vn.ticketCanAdvanceConfig (
id int(10) unsigned NOT NULL,
destinationOrder INT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `ticketCanAdvanceConfig_check` CHECK (`id` = 1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO vn.ticketCanAdvanceConfig SET id =1, destinationOrder = 5;

View File

@ -687,8 +687,8 @@ export default {
ticketFuture: { ticketFuture: {
searchResult: 'vn-ticket-future tbody tr', searchResult: 'vn-ticket-future tbody tr',
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
originDated: 'vn-date-picker[label="Origin date"]', originScopeDays: 'vn-date-picker[label="Origin date"]',
futureDated: 'vn-date-picker[label="Destination date"]', futureScopeDays: 'vn-date-picker[label="Destination date"]',
linesMax: 'vn-textfield[label="Max Lines"]', linesMax: 'vn-textfield[label="Max Lines"]',
litersMax: 'vn-textfield[label="Max Liters"]', litersMax: 'vn-textfield[label="Max Liters"]',
ipt: 'vn-autocomplete[label="Origin IPT"]', ipt: 'vn-autocomplete[label="Origin IPT"]',

View File

@ -30,18 +30,18 @@ describe('Ticket Future path', () => {
expect(message.text).toContain('warehouseFk is a required argument'); expect(message.text).toContain('warehouseFk is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.futureDated); await page.clearInput(selectors.ticketFuture.futureScopeDays);
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar(); message = await page.waitForSnackbar();
expect(message.text).toContain('futureDated is a required argument'); expect(message.text).toContain('futureScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.originDated); await page.clearInput(selectors.ticketFuture.originScopeDays);
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar(); message = await page.waitForSnackbar();
expect(message.text).toContain('originDated is a required argument'); expect(message.text).toContain('originScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
@ -71,7 +71,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free'); await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('state=FREE'); expect(httpRequest).toContain('state=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
@ -80,7 +80,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free'); await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureState=FREE'); expect(httpRequest).toContain('futureState=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state); await page.clearInput(selectors.ticketFuture.state);

View File

@ -33,7 +33,7 @@ module.exports = function(Self) {
const defaultLimit = this.app.orm.selectLimit; const defaultLimit = this.app.orm.selectLimit;
const filter = ctx.args.filter || {limit: defaultLimit}; const filter = ctx.args.filter || {limit: defaultLimit};
if (filter.limit > defaultLimit) { if (!filter.limit || filter.limit > defaultLimit) {
filter.limit = defaultLimit; filter.limit = defaultLimit;
ctx.args.filter = filter; ctx.args.filter = filter;
} }
@ -349,9 +349,10 @@ module.exports = function(Self) {
}, },
hasFilter(ctx) { hasFilter(ctx) {
return ctx.req.method.toUpperCase() === 'GET' && const {method, req} = ctx;
ctx.method.accepts.some(x => x.arg === 'filter' && x.type.toLowerCase() === 'object'); if (method.noLimit) return false;
} return req.method.toUpperCase() === 'GET' &&
method.accepts.some(x => x.arg === 'filter' && x.type.toLowerCase() === 'object');
},
}); });
}; };

View File

@ -372,5 +372,7 @@
"The entry not have stickers": "La entrada no tiene etiquetas", "The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros", "Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada", "Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe" "The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento"
} }

View File

@ -16,7 +16,8 @@ module.exports = Self => {
http: { http: {
path: `/getBalance`, path: `/getBalance`,
verb: 'GET' verb: 'GET'
} },
noLimit: true
}); });
Self.getBalance = async(ctx, filter, options) => { Self.getBalance = async(ctx, filter, options) => {

View File

@ -105,13 +105,12 @@ module.exports = Self => {
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`CALL vn.ticket_canAdvance(?,?,?)`, `CALL vn.ticket_canAdvance(?,?,?)`,
[args.dateFuture, args.dateToAdvance, args.warehouseFk]); [args.dateFuture, args.dateToAdvance, args.warehouseFk]
);
stmts.push(stmt); stmts.push(stmt);
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`SELECT f.* FROM tmp.filter f`);
SELECT f.*
FROM tmp.filter f`);
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
@ -119,9 +118,7 @@ module.exports = Self => {
stmt.merge(conn.makeLimit(filter)); stmt.merge(conn.makeLimit(filter));
const ticketsIndex = stmts.push(stmt) - 1; const ticketsIndex = stmts.push(stmt) - 1;
stmts.push( stmts.push(`DROP TEMPORARY TABLE tmp.filter`);
`DROP TEMPORARY TABLE
tmp.filter`);
const sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions); const result = await conn.executeStmt(sql, myOptions);

View File

@ -9,13 +9,13 @@ module.exports = Self => {
accessType: 'READ', accessType: 'READ',
accepts: [ accepts: [
{ {
arg: 'originDated', arg: 'originScopeDays',
type: 'date', type: 'date',
description: 'The date in question', description: 'The date in question',
required: true required: true
}, },
{ {
arg: 'futureDated', arg: 'futureScopeDays',
type: 'date', type: 'date',
description: 'The date to probe', description: 'The date to probe',
required: true required: true
@ -129,9 +129,9 @@ module.exports = Self => {
] ]
}; };
case 'state': case 'state':
return {'f.stateCode': {like: `%${value}%`}}; return {'f.alertLevel': value};
case 'futureState': case 'futureState':
return {'f.futureStateCode': {like: `%${value}%`}}; return {'f.futureAlertLevel': value};
} }
}); });
@ -141,7 +141,7 @@ module.exports = Self => {
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`CALL vn.ticket_canbePostponed(?,?,?)`, `CALL vn.ticket_canbePostponed(?,?,?)`,
[args.originDated, args.futureDated, args.warehouseFk]); [args.originScopeDays, args.futureScopeDays, args.warehouseFk]);
stmts.push(stmt); stmts.push(stmt);
@ -170,7 +170,7 @@ module.exports = Self => {
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id
`); `);
if (args.problems != undefined && (!args.originDated && !args.futureDated)) if (args.problems != undefined && (!args.originScopeDays && !args.futureScopeDays))
throw new UserError('Choose a date range or days forward'); throw new UserError('Choose a date range or days forward');
let condition; let condition;

View File

@ -138,7 +138,7 @@ module.exports = Self => {
id: id, id: id,
url: `${url}ticket/${id}/summary` url: `${url}ticket/${id}/summary`
}); });
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message); await models.Chat.sendCheckingPresence(ctx, salesPersonUser.id, message);
} }
const updatedTicket = await ticket.updateAttribute('isDeleted', true, myOptions); const updatedTicket = await ticket.updateAttribute('isDeleted', true, myOptions);

View File

@ -0,0 +1,86 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('setWeight', {
description: 'Sets weight of a ticket',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The ticket id',
http: {source: 'path'}
}, {
arg: 'weight',
type: 'number',
required: true,
description: 'The weight value',
}],
returns: {
type: 'Array',
root: true
},
http: {
path: `/:id/setWeight`,
verb: 'POST'
}
});
Self.setWeight = async(ctx, ticketId, weight, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
let tx;
if (typeof options == 'object') Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const ticket = await Self.findById(ticketId, null, myOptions);
if (ticket.weight) throw new UserError('Weight already set');
const canEdit = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'updateAttributes');
const client = await models.Client.findById(ticket.clientFk, {
include: {relation: 'salesPersonUser'}},
myOptions);
if (!canEdit) {
const salesPersonUser = client.salesPersonUser();
const workerDepartments = await models.WorkerDepartment.find({
include: {relation: 'department'},
where: {workerFk: {inq: [userId, salesPersonUser.id]}}
}, myOptions);
if (
workerDepartments.length == 2 &&
workerDepartments[0].departmentFk != workerDepartments[1].departmentFk
)
throw new UserError('This ticket is not allocated to your department');
}
await ticket.updateAttribute('weight', weight, myOptions);
const packedState = await models.State.findOne({where: {code: 'PACKED'}}, myOptions);
const ticketState = await models.TicketState.findOne({where: {ticketFk: ticketId}}, myOptions);
const [{taxArea}] = await Self.rawSql('SELECT clientTaxArea(?,?) taxArea',
[ticket.clientFk, ticket.warehouseFk], myOptions);
const isInvoiceable = ticketState.alertLevel >= packedState.alertLevel &&
taxArea == 'WORLD' && client.hasDailyInvoice;
if (tx) await tx.commit();
let invoiceIds = [];
if (isInvoiceable) invoiceIds = [...await Self.invoiceTicketsAndPdf(ctx, [ticketId])];
return invoiceIds;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -12,8 +12,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
}; };
@ -35,8 +35,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
problems: true problems: true
}; };
@ -60,8 +60,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
problems: false problems: false
}; };
@ -85,8 +85,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
problems: null problems: null
}; };
@ -110,8 +110,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
state: 'OK' state: 'OK'
}; };
@ -135,8 +135,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
futureState: 'OK' futureState: 'OK'
}; };
@ -160,8 +160,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
ipt: null ipt: null
}; };
@ -185,8 +185,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
ipt: 'H' ipt: 'H'
}; };
@ -210,8 +210,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
futureIpt: null futureIpt: null
}; };
@ -235,8 +235,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
futureIpt: 'H' futureIpt: 'H'
}; };
@ -260,8 +260,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
id: 13 id: 13
}; };
@ -285,8 +285,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: 1, warehouseFk: 1,
futureId: 12 futureId: 12
}; };

View File

@ -0,0 +1,70 @@
const {models} = require('vn-loopback/server/server');
describe('ticket setWeight()', () => {
const ctx = beforeAll.getCtx();
beforeAll.mockLoopBackContext();
let opts;
let tx;
const employeeId = 1;
const administrativeId = 5;
beforeEach(async() => {
opts = {transaction: tx};
tx = await models.Ticket.beginTransaction({});
opts.transaction = tx;
ctx.req.accessToken.userId = administrativeId;
});
afterEach(async() => await tx.rollback());
it('should throw an error if the weight is already set', async() => {
try {
const ticketId = 1;
const weight = 10;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
} catch (e) {
expect(e.message).toEqual('Weight already set');
}
});
it('should set the weight of a ticket', async() => {
const ticketId = 31;
const weight = 15;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
const ticket = await models.Ticket.findById(ticketId, null, opts);
expect(ticket.weight).toEqual(weight);
});
it('should throw an error if the user is not allocated to the same department', async() => {
ctx.req.accessToken.userId = employeeId;
try {
const ticketId = 10;
const weight = 20;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
} catch (e) {
expect(e.message).toEqual('This ticket is not allocated to your department');
}
});
it('should call invoiceTicketsAndPdf if the ticket is invoiceable', async() => {
const ticketId = 10;
const weight = 25;
spyOn(models.Client, 'findById').and.returnValue({
hasDailyInvoice: true,
salesPersonUser: () => ({id: 1})
});
spyOn(models.TicketState, 'findOne').and.returnValue({alertLevel: 3});
spyOn(models.Ticket, 'rawSql').and.returnValue([{taxArea: 'WORLD'}]);
spyOn(models.Ticket, 'invoiceTicketsAndPdf').and.returnValue([10]);
const invoiceIds = await models.Ticket.setWeight(ctx, ticketId, weight, opts);
expect(invoiceIds.length).toBeGreaterThan(0);
});
});

View File

@ -46,4 +46,5 @@ module.exports = function(Self) {
require('../methods/ticket/invoiceTicketsAndPdf')(Self); require('../methods/ticket/invoiceTicketsAndPdf')(Self);
require('../methods/ticket/docuwareDownload')(Self); require('../methods/ticket/docuwareDownload')(Self);
require('../methods/ticket/myLastModified')(Self); require('../methods/ticket/myLastModified')(Self);
require('../methods/ticket/setWeight')(Self);
}; };

View File

@ -39,14 +39,8 @@
<thead> <thead>
<tr second-header> <tr second-header>
<td></td> <td></td>
<th colspan="7"> <th colspan="9" translate>Destination</th>
<span translate>Destination</span> <th colspan="11" translate>Origin</th>
{{model.userParams.dateToAdvance| date: 'dd/MM/yyyy'}}
</th>
<th colspan="11">
<span translate>Origin</span>
{{model.userParams.dateFuture | date: 'dd/MM/yyyy'}}
</th>
</tr> </tr>
<tr> <tr>
<th shrink> <th shrink>
@ -56,14 +50,16 @@
check-field="checked"> check-field="checked">
</vn-multi-check> </vn-multi-check>
</th> </th>
<th shrink> <th shrink></th>
</th>
<th field="id"> <th field="id">
<span translate>ID</span> <span translate>ID</span>
</th> </th>
<th field="ipt" title="{{'Item Packing Type' | translate}}"> <th field="ipt" title="{{'Item Packing Type' | translate}}">
<span>IPT</span> <span>IPT</span>
</th> </th>
<th field="preparation">
<span translate>Preparation</span>
</th>
<th field="state"> <th field="state">
<span translate>State</span> <span translate>State</span>
</th> </th>
@ -125,6 +121,7 @@
</span> </span>
</td> </td>
<td>{{::ticket.ipt | dashIfEmpty}}</td> <td>{{::ticket.ipt | dashIfEmpty}}</td>
<td>{{::ticket.preparation | dashIfEmpty}}</td>
<td> <td>
<span <span
class="chip {{ticket.classColor}}"> class="chip {{ticket.classColor}}">
@ -165,7 +162,6 @@
{{::(ticket.futureTotalWithVat ? ticket.futureTotalWithVat : 0) | currency: 'EUR': 2}} {{::(ticket.futureTotalWithVat ? ticket.futureTotalWithVat : 0) | currency: 'EUR': 2}}
</span> </span>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -9,13 +9,13 @@
<vn-date-picker <vn-date-picker
vn-one vn-one
label="Origin date" label="Origin date"
ng-model="filter.originDated" ng-model="filter.originScopeDays"
required="true"> required="true">
</vn-date-picker> </vn-date-picker>
<vn-date-picker <vn-date-picker
vn-one vn-one
label="Destination date" label="Destination date"
ng-model="filter.futureDated" ng-model="filter.futureScopeDays"
required="true"> required="true">
</vn-date-picker> </vn-date-picker>
</vn-horizontal> </vn-horizontal>
@ -59,7 +59,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
data="$ctrl.groupedStates" data="$ctrl.groupedStates"
label="Origin Grouped State" label="Origin Grouped State"
value-field="code" value-field="id"
show-field="name" show-field="name"
ng-model="filter.state"> ng-model="filter.state">
<tpl-item> <tpl-item>
@ -69,7 +69,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
data="$ctrl.groupedStates" data="$ctrl.groupedStates"
label="Destination Grouped State" label="Destination Grouped State"
value-field="code" value-field="id"
show-field="name" show-field="name"
ng-model="filter.futureState"> ng-model="filter.futureState">
<tpl-item> <tpl-item>

View File

@ -65,8 +65,8 @@ export default class Controller extends Section {
this.$http.get(`UserConfigs/getUserConfig`) this.$http.get(`UserConfigs/getUserConfig`)
.then(res => { .then(res => {
this.filterParams = { this.filterParams = {
originDated: today, originScopeDays: today,
futureDated: today, futureScopeDays: today,
warehouseFk: res.data.warehouseFk warehouseFk: res.data.warehouseFk
}; };
this.$.model.applyFilter(null, this.filterParams); this.$.model.applyFilter(null, this.filterParams);

View File

@ -14,3 +14,4 @@ Success: Tickets movidos correctamente
IPT: Encajado IPT: Encajado
Origin Date: Fecha origen Origin Date: Fecha origen
Destination Date: Fecha destino Destination Date: Fecha destino
Preparation: Preparación

View File

@ -4,3 +4,4 @@ Client phone: Tel. cliente
Client mobile: Móv. cliente Client mobile: Móv. cliente
Go to the ticket: Ir al ticket Go to the ticket: Ir al ticket
Change state: Cambiar estado Change state: Cambiar estado
Weight: Peso