diff --git a/.vscode/settings.json b/.vscode/settings.json index 899dfc788..40ec5c0d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,9 @@ "[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, "cSpell.words": [ "salix", "fdescribe" diff --git a/back/tests.js b/back/tests.js index 97e548d33..efade4d7d 100644 --- a/back/tests.js +++ b/back/tests.js @@ -7,6 +7,10 @@ process.on('warning', warning => { console.log(warning.stack); }); +process.on('SIGUSR2', async() => { + if (container) await container.rm(); +}); + process.on('exit', async function() { if (container) await container.rm(); }); diff --git a/db/changes/235001/00-silexToSalix.sql b/db/changes/235001/00-silexToSalix.sql new file mode 100644 index 000000000..bad430ac2 --- /dev/null +++ b/db/changes/235001/00-silexToSalix.sql @@ -0,0 +1,46 @@ + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`delivery_beforeInsert` + BEFORE INSERT ON `delivery` + FOR EACH ROW +BEGIN + + IF (NEW.longitude IS NOT NULL AND NEW.latitude IS NOT NULL AND NEW.ticketFK IS NOT NULL) + THEN + UPDATE address + SET longitude = NEW.longitude, + latitude = NEW.latitude + WHERE id IN ( + SELECT addressFK + FROM ticket + WHERE id = NEW.ticketFk + ); + END IF; + +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`delivery_beforeUpdate` + BEFORE UPDATE ON `delivery` + FOR EACH ROW +BEGIN + +IF (NEW.longitude IS NOT NULL AND NEW.latitude IS NOT NULL AND NEW.ticketFK IS NOT NULL) + THEN + UPDATE address + SET longitude = NEW.longitude, + latitude = NEW.latitude + WHERE id IN ( + SELECT addressFK + FROM ticket + WHERE id = NEW.ticketFk + ); + END IF; + +END$$ +DELIMITER ; + + +ALTER TABLE `vn`.`address` MODIFY COLUMN longitude decimal(11,7) DEFAULT NULL NULL COMMENT 'Indica la última longitud proporcionada por tabla delivery'; +ALTER TABLE `vn`.`address` MODIFY COLUMN latitude decimal(11,7) DEFAULT NULL NULL COMMENT 'Indica la última latitud proporcionada por tabla delivery'; diff --git a/db/changes/235201/00-alterTable.sql b/db/changes/235201/00-alterTable.sql new file mode 100644 index 000000000..b6974b715 --- /dev/null +++ b/db/changes/235201/00-alterTable.sql @@ -0,0 +1 @@ +ALTER TABLE `vn`.`ticketTracking` CHANGE `workerFk` `userFk` int(10) unsigned DEFAULT NULL NULL; \ No newline at end of file diff --git a/db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql b/db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql new file mode 100644 index 000000000..bf4cc6002 --- /dev/null +++ b/db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql @@ -0,0 +1,4 @@ +RENAME TABLE `vn`.`clientCreditLimit` TO `vn`.`roleCreditLimit`; +ALTER TABLE `vn`.`clientCreditLimit` DROP FOREIGN KEY `clientCreditLimit_FK`; +ALTER TABLE `vn`.`roleCreditLimit` ADD CONSTRAINT `roleCreditLimit_FK` FOREIGN KEY (`roleFk`) REFERENCES `account`.`role`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + diff --git a/db/changes/235201/01-procedures.sql b/db/changes/235201/01-procedures.sql new file mode 100644 index 000000000..3777708d5 --- /dev/null +++ b/db/changes/235201/01-procedures.sql @@ -0,0 +1,1129 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `hedera`.`order_confirmWithUser`(vSelf INT, vUserId INT) +BEGIN +/** + * Confirms an order, creating each of its tickets on the corresponding + * date, store and user. + * + * @param vSelf The order identifier + * @param vUser The user identifier + */ + DECLARE vOk BOOL; + DECLARE vDone BOOL DEFAULT FALSE; + DECLARE vWarehouse INT; + DECLARE vShipment DATE; + DECLARE vTicket INT; + DECLARE vNotes VARCHAR(255); + DECLARE vItem INT; + DECLARE vConcept VARCHAR(30); + DECLARE vAmount INT; + DECLARE vPrice DECIMAL(10,2); + DECLARE vSale INT; + DECLARE vRate INT; + DECLARE vRowId INT; + DECLARE vPriceFixed DECIMAL(10,2); + DECLARE vDelivery DATE; + DECLARE vAddress INT; + DECLARE vIsConfirmed BOOL; + DECLARE vClientId INT; + DECLARE vCompanyId INT; + DECLARE vAgencyModeId INT; + DECLARE TICKET_FREE INT DEFAULT 2; + DECLARE vCalc INT; + DECLARE vIsLogifloraItem BOOL; + DECLARE vOldQuantity INT; + DECLARE vNewQuantity INT; + DECLARE vIsTaxDataChecked BOOL; + + DECLARE cDates CURSOR FOR + SELECT zgs.shipped, r.warehouse_id + FROM `order` o + JOIN order_row r ON r.order_id = o.id + LEFT JOIN tmp.zoneGetShipped zgs ON zgs.warehouseFk = r.warehouse_id + WHERE o.id = vSelf AND r.amount != 0 + GROUP BY r.warehouse_id; + + DECLARE cRows CURSOR FOR + SELECT r.id, r.item_id, i.name, r.amount, r.price, r.rate, i.isFloramondo + FROM order_row r + JOIN vn.item i ON i.id = r.item_id + WHERE r.amount != 0 + AND r.warehouse_id = vWarehouse + AND r.order_id = vSelf + ORDER BY r.rate DESC; + + DECLARE CONTINUE HANDLER FOR NOT FOUND + SET vDone = TRUE; + + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + -- Carga los datos del pedido + SELECT o.date_send, o.address_id, o.note, a.clientFk, + o.company_id, o.agency_id, c.isTaxDataChecked + INTO vDelivery, vAddress, vNotes, vClientId, + vCompanyId, vAgencyModeId, vIsTaxDataChecked + FROM hedera.`order` o + JOIN vn.address a ON a.id = o.address_id + JOIN vn.client c ON c.id = a.clientFk + WHERE o.id = vSelf; + + -- Verifica si el cliente tiene los datos comprobados + IF NOT vIsTaxDataChecked THEN + CALL util.throw ('clientNotVerified'); + END IF; + + -- Carga las fechas de salida de cada almacen + CALL vn.zone_getShipped (vDelivery, vAddress, vAgencyModeId, FALSE); + + -- Trabajador que realiza la accion + IF vUserId IS NULL THEN + SELECT employeeFk INTO vUserId FROM orderConfig; + END IF; + + START TRANSACTION; + + CALL order_checkEditable(vSelf); + + -- Check order is not empty + + SELECT COUNT(*) > 0 INTO vOk + FROM order_row WHERE order_id = vSelf AND amount > 0; + + IF NOT vOk THEN + CALL util.throw ('ORDER_EMPTY'); + END IF; + + -- Crea los tickets del pedido + + OPEN cDates; + + lDates: + LOOP + SET vTicket = NULL; + SET vDone = FALSE; + FETCH cDates INTO vShipment, vWarehouse; + + IF vDone THEN + LEAVE lDates; + END IF; + + -- Busca un ticket existente que coincida con los parametros + WITH tPrevia AS + (SELECT DISTINCT s.ticketFk + FROM vn.sale s + JOIN vn.saleGroupDetail sgd ON sgd.saleFk = s.id + JOIN vn.ticket t ON t.id = s.ticketFk + WHERE t.shipped BETWEEN vShipment AND util.dayend(vShipment) + ) + SELECT t.id INTO vTicket + FROM vn.ticket t + LEFT JOIN tPrevia tp ON tp.ticketFk = t.id + LEFT JOIN vn.ticketState tls on tls.ticket = t.id + JOIN hedera.`order` o + ON o.address_id = t.addressFk + AND vWarehouse = t.warehouseFk + AND o.date_send = t.landed + AND DATE(t.shipped) = vShipment + WHERE o.id = vSelf + AND t.refFk IS NULL + AND tp.ticketFk IS NULL + AND IFNULL(tls.alertLevel,0) = 0 + LIMIT 1; + + -- Crea el ticket en el caso de no existir uno adecuado + IF vTicket IS NULL + THEN + + SET vShipment = IFNULL(vShipment, util.VN_CURDATE()); + + CALL vn.ticket_add( + vClientId, + vShipment, + vWarehouse, + vCompanyId, + vAddress, + vAgencyModeId, + NULL, + vDelivery, + vUserId, + TRUE, + vTicket + ); + ELSE + INSERT INTO vn.ticketTracking + SET ticketFk = vTicket, + userFk = vUserId, + stateFk = TICKET_FREE; + END IF; + + INSERT IGNORE INTO vn.orderTicket + SET orderFk = vSelf, + ticketFk = vTicket; + + -- Añade las notas + + IF vNotes IS NOT NULL AND vNotes != '' + THEN + INSERT INTO vn.ticketObservation SET + ticketFk = vTicket, + observationTypeFk = 4 /* salesperson */, + `description` = vNotes + ON DUPLICATE KEY UPDATE + `description` = CONCAT(VALUES(`description`),'. ', `description`); + END IF; + + -- Añade los movimientos y sus componentes + + OPEN cRows; + + lRows: LOOP + SET vDone = FALSE; + FETCH cRows INTO vRowId, vItem, vConcept, vAmount, vPrice, vRate, vIsLogifloraItem; + + IF vDone THEN + LEAVE lRows; + END IF; + + SET vSale = NULL; + + SELECT s.id, s.quantity INTO vSale, vOldQuantity + FROM vn.sale s + WHERE ticketFk = vTicket + AND price = vPrice + AND itemFk = vItem + AND discount = 0 + LIMIT 1; + + IF vSale THEN + UPDATE vn.sale + SET quantity = quantity + vAmount, + originalQuantity = quantity + WHERE id = vSale; + + SELECT s.quantity INTO vNewQuantity + FROM vn.sale s + WHERE id = vSale; + ELSE + -- Obtiene el coste + SELECT SUM(rc.`price`) valueSum INTO vPriceFixed + FROM orderRowComponent rc + JOIN vn.component c ON c.id = rc.componentFk + JOIN vn.componentType ct ON ct.id = c.typeFk AND ct.isBase + WHERE rc.rowFk = vRowId; + + INSERT INTO vn.sale + SET itemFk = vItem, + ticketFk = vTicket, + concept = vConcept, + quantity = vAmount, + price = vPrice, + priceFixed = vPriceFixed, + isPriceFixed = TRUE; + + SET vSale = LAST_INSERT_ID(); + + INSERT INTO vn.saleComponent + (saleFk, componentFk, `value`) + SELECT vSale, rc.componentFk, rc.price + FROM orderRowComponent rc + JOIN vn.component c ON c.id = rc.componentFk + WHERE rc.rowFk = vRowId + GROUP BY vSale, rc.componentFk; + END IF; + + UPDATE order_row SET Id_Movimiento = vSale + WHERE id = vRowId; + + -- Inserta en putOrder si la compra es de Floramondo + IF vIsLogifloraItem THEN + CALL cache.availableNoRaids_refresh(vCalc,FALSE,vWarehouse,vShipment); + + SET @available := 0; + + SELECT GREATEST(0,available) INTO @available + FROM cache.availableNoRaids + WHERE calc_id = vCalc + AND item_id = vItem; + + UPDATE cache.availableNoRaids + SET available = GREATEST(0,available - vAmount) + WHERE item_id = vItem + AND calc_id = vCalc; + + INSERT INTO edi.putOrder ( + deliveryInformationID, + supplyResponseId, + quantity , + EndUserPartyId, + EndUserPartyGLN, + FHAdminNumber, + saleFk + ) + SELECT di.ID, + i.supplyResponseFk, + CEIL((vAmount - @available)/ sr.NumberOfItemsPerCask), + o.address_id , + vClientId, + IFNULL(ca.fhAdminNumber, fhc.defaultAdminNumber), + vSale + FROM edi.deliveryInformation di + JOIN vn.item i ON i.supplyResponseFk = di.supplyResponseID + JOIN edi.supplyResponse sr ON sr.ID = i.supplyResponseFk + LEFT JOIN edi.clientFHAdminNumber ca ON ca.clientFk = vClientId + JOIN edi.floraHollandConfig fhc + JOIN hedera.`order` o ON o.id = vSelf + WHERE i.id = vItem + AND di.LatestOrderDateTime > util.VN_NOW() + AND vAmount > @available + LIMIT 1; + END IF; + END LOOP; + + CLOSE cRows; + END LOOP; + + CLOSE cDates; + + UPDATE `order` SET confirmed = TRUE, confirm_date = util.VN_NOW() + WHERE id = vSelf; + + COMMIT; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`( + vSerial VARCHAR(255), + vInvoiceDate DATE, + vTaxArea VARCHAR(25), + OUT vNewInvoiceId INT) +BEGIN +/** + * Creación de facturas emitidas. + * requiere previamente tabla tmp.ticketToInvoice(id). + * + * @param vSerial serie a la cual se hace la factura + * @param vInvoiceDate fecha de la factura + * @param vTaxArea tipo de iva en relacion a la empresa y al cliente + * @param vNewInvoiceId id de la factura que se acaba de generar + * @return vNewInvoiceId + */ + DECLARE vIsAnySaleToInvoice BOOL; + DECLARE vIsAnyServiceToInvoice BOOL; + DECLARE vNewRef VARCHAR(255); + DECLARE vWorker INT DEFAULT account.myUser_getId(); + DECLARE vCompanyFk INT; + DECLARE vInterCompanyFk INT; + DECLARE vClientFk INT; + DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1; + DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6; + DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2; + DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R'; + DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S'; + DECLARE vNewInvoiceInFk INT; + DECLARE vIsInterCompany BOOL DEFAULT FALSE; + DECLARE vIsCEESerial BOOL DEFAULT FALSE; + DECLARE vIsCorrectInvoiceDate BOOL; + DECLARE vMaxShipped DATE; + DECLARE vDone BOOL; + DECLARE vTicketFk INT; + DECLARE vCursor CURSOR FOR + SELECT id + FROM tmp.ticketToInvoice; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + + SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE()); + + SELECT t.clientFk, + t.companyFk, + MAX(DATE(t.shipped)), + DATE(vInvoiceDate) >= invoiceOut_getMaxIssued( + vSerial, + t.companyFk, + YEAR(vInvoiceDate)) + INTO vClientFk, + vCompanyFk, + vMaxShipped, + vIsCorrectInvoiceDate + FROM tmp.ticketToInvoice tt + JOIN ticket t ON t.id = tt.id; + + IF(vMaxShipped > vInvoiceDate) THEN + CALL util.throw("Invoice date can't be less than max date"); + END IF; + + IF NOT vIsCorrectInvoiceDate THEN + CALL util.throw('Exists an invoice with a previous date'); + END IF; + + -- Eliminem de tmp.ticketToInvoice els tickets que no han de ser facturats + DELETE ti.* + FROM tmp.ticketToInvoice ti + JOIN ticket t ON t.id = ti.id + JOIN sale s ON s.ticketFk = t.id + JOIN item i ON i.id = s.itemFk + JOIN supplier su ON su.id = t.companyFk + JOIN client c ON c.id = t.clientFk + LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk + WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted) + OR c.isTaxDataChecked = FALSE + OR t.isDeleted + OR c.hasToInvoice = FALSE + OR itc.id IS NULL; + + SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0 + INTO vIsAnySaleToInvoice + FROM tmp.ticketToInvoice t + JOIN sale s ON s.ticketFk = t.id; + + SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice + FROM tmp.ticketToInvoice t + JOIN ticketService ts ON ts.ticketFk = t.id; + + IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice) + AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase()) + THEN + + -- el trigger añade el siguiente Id_Factura correspondiente a la vSerial + INSERT INTO invoiceOut( + ref, + serial, + issued, + clientFk, + dued, + companyFk, + siiTypeInvoiceOutFk + ) + SELECT + 1, + vSerial, + vInvoiceDate, + vClientFk, + getDueDate(vInvoiceDate, dueDay), + vCompanyFk, + IF(vSerial = vCorrectingSerial, + vCplusCorrectingInvoiceTypeFk, + IF(vSerial = vSimplifiedSerial, + vCplusSimplifiedInvoiceTypeFk, + vCplusStandardInvoiceTypeFk)) + FROM client + WHERE id = vClientFk; + + SET vNewInvoiceId = LAST_INSERT_ID(); + + SELECT `ref` + INTO vNewRef + FROM invoiceOut + WHERE id = vNewInvoiceId; + + OPEN vCursor; + l: LOOP + SET vDone = FALSE; + FETCH vCursor INTO vTicketFk; + + IF vDone THEN + LEAVE l; + END IF; + + CALL ticket_recalc(vTicketFk, vTaxArea); + + END LOOP; + CLOSE vCursor; + + UPDATE ticket t + JOIN tmp.ticketToInvoice ti ON ti.id = t.id + SET t.refFk = vNewRef; + + DROP TEMPORARY TABLE IF EXISTS tmp.updateInter; + CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY + SELECT s.id, ti.id ticket_id, vWorker Id_Trabajador + FROM tmp.ticketToInvoice ti + LEFT JOIN ticketState ts ON ti.id = ts.ticket + JOIN state s + WHERE IFNULL(ts.alertLevel, 0) < 3 and s.`code` = getAlert3State(ti.id); + + INSERT INTO ticketTracking(stateFk, ticketFk, userFk) + SELECT * FROM tmp.updateInter; + + CALL invoiceExpenseMake(vNewInvoiceId); + CALL invoiceTaxMake(vNewInvoiceId, vTaxArea); + + UPDATE invoiceOut io + JOIN ( + SELECT SUM(amount) total + FROM invoiceOutExpense + WHERE invoiceOutFk = vNewInvoiceId + ) base + JOIN ( + SELECT SUM(vat) total + FROM invoiceOutTax + WHERE invoiceOutFk = vNewInvoiceId + ) vat + SET io.amount = base.total + vat.total + WHERE io.id = vNewInvoiceId; + + DROP TEMPORARY TABLE tmp.updateInter; + + SELECT COUNT(*), id + INTO vIsInterCompany, vInterCompanyFk + FROM company + WHERE clientFk = vClientFk; + + IF (vIsInterCompany) THEN + + INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk) + SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk; + + SET vNewInvoiceInFk = LAST_INSERT_ID(); + + DROP TEMPORARY TABLE IF EXISTS tmp.ticket; + CREATE TEMPORARY TABLE tmp.ticket + (KEY (ticketFk)) + ENGINE = MEMORY + SELECT id ticketFk + FROM tmp.ticketToInvoice; + + CALL `ticket_getTax`('NATIONAL'); + + SET @vTaxableBaseServices := 0.00; + SET @vTaxCodeGeneral := NULL; + + INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk) + SELECT vNewInvoiceInFk, + @vTaxableBaseServices, + sub.expenseFk, + sub.taxTypeSageFk, + sub.transactionTypeSageFk + FROM ( + SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase, + i.expenseFk, + i.taxTypeSageFk, + i.transactionTypeSageFk, + @vTaxCodeGeneral := i.taxClassCodeFk + FROM tmp.ticketServiceTax tst + JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code + WHERE i.isService + HAVING taxableBase + ) sub; + + INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk) + SELECT vNewInvoiceInFk, + SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral, + @vTaxableBaseServices, 0) taxableBase, + i.expenseFk, + i.taxTypeSageFk , + i.transactionTypeSageFk + FROM tmp.ticketTax tt + JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code + WHERE !i.isService + GROUP BY tt.pgcFk + HAVING taxableBase + ORDER BY tt.priority; + + CALL invoiceInDueDay_calculate(vNewInvoiceInFk); + + SELECT COUNT(*) INTO vIsCEESerial + FROM invoiceOutSerial + WHERE code = vSerial; + + IF vIsCEESerial THEN + + INSERT INTO invoiceInIntrastat ( + invoiceInFk, + intrastatFk, + amount, + stems, + countryFk, + net) + SELECT + vNewInvoiceInFk, + i.intrastatFk, + SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))), + SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))), + su.countryFk, + CAST(SUM(IFNULL(i.stems, 1) + * s.quantity + * IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2)) + FROM sale s + JOIN ticket t ON s.ticketFk = t.id + JOIN supplier su ON su.id = t.companyFk + JOIN item i ON i.id = s.itemFk + LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk + WHERE t.refFk = vNewRef + GROUP BY i.intrastatFk; + + END IF; + DROP TEMPORARY TABLE tmp.ticket; + DROP TEMPORARY TABLE tmp.ticketAmount; + DROP TEMPORARY TABLE tmp.ticketTax; + DROP TEMPORARY TABLE tmp.ticketServiceTax; + END IF; + END IF; + DROP TEMPORARY TABLE `tmp`.`ticketToInvoice`; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`productionError_add`() +BEGIN + DECLARE vDatedFrom DATETIME; + DECLARE vDatedTo DATETIME; +/** + * Rellena la tabla vn.productionError con estadisticas de encajadores, revisores y sacadores. Se ejecuta en el nightTask + */ + SELECT util.VN_CURDATE() - INTERVAL 1 DAY, util.dayend(util.VN_CURDATE() - INTERVAL 1 DAY) INTO vDatedFrom, vDatedTo; + CALL timeControl_calculateAll(vDatedFrom, vDatedTo); + + -- Rellena la tabla tmp.errorsByClaim con encajadores, revisores y sacadores + CREATE OR REPLACE TEMPORARY TABLE tmp.errorsByClaim + ENGINE = MEMORY + SELECT COUNT(c.ticketFk) errors, + cd.workerFk + FROM claimDevelopment cd + JOIN claim c ON cd.claimFk = c.id + JOIN ticket t ON c.ticketFk = t.id + JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id + WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo + AND cr.code IN ('pic', 'chk', 'pck') + GROUP BY cd.workerFk; + + -- Genera la tabla tmp.volume con encajadores, sacadores y revisores + CREATE OR REPLACE TEMPORARY TABLE tmp.volume + ENGINE = MEMORY + SELECT SUM(w.volume) volume, + w.workerFk + FROM bs.workerProductivity w + WHERE w.dated BETWEEN vDatedFrom AND vDatedTo + GROUP BY w.workerFk; + + -- Rellena la tabla tmp.errorsByChecker con fallos de revisores + CREATE OR REPLACE TEMPORARY TABLE tmp.errorsByChecker + ENGINE = MEMORY + SELECT st.workerFk, + COUNT(t.id) errors + FROM saleMistake sm + JOIN saleTracking st ON sm.saleFk = st.saleFk + JOIN `state` s2 ON s2.id = st.stateFk + JOIN sale s ON s.id = sm.saleFk + JOIN ticket t on t.id = s.ticketFk + WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo) + AND s2.code IN ('OK','PREVIOUS_PREPARATION','PREPARED','CHECKED') + GROUP BY st.workerFk; + + -- Rellena la tabla tmp.expeditionErrors con fallos de expediciones + CREATE OR REPLACE TEMPORARY TABLE tmp.expeditionErrors + ENGINE = MEMORY + SELECT COUNT(t.id) errors, + e.workerFk + FROM vn.expeditionMistake pm + JOIN vn.expedition e ON e.id = pm.expeditionFk + JOIN vn.ticket t ON t.id = e.ticketFk + WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo + GROUP BY e.workerFk; + + -- Genera la tabla tmp.total para sacadores y revisores + CREATE OR REPLACE TEMPORARY TABLE tmp.total + ENGINE = MEMORY + SELECT st.workerFk, + COUNT(DISTINCT t.id) ticketCount, + COUNT(s.id) lineCount + FROM saleTracking st + JOIN `state` s2 ON s2.id = st.stateFk + JOIN sale s ON s.id = st.saleFk + JOIN ticket t ON s.ticketFk = t.id + WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo) + AND s2.code IN ('OK','PREVIOUS_PREPARATION','PREPARED','CHECKED') + GROUP BY st.workerFk; + + -- Rellena la tabla vn.productionError con sacadores + INSERT INTO productionError(userFk, + firstname, + lastname, + rol, + ticketNumber, + lineNumber, + error, + volume, + hourStart, + hourEnd, + hourWorked, + dated) + SELECT w.id, + w.firstName, + w.lastName, + "Sacadores", + t.ticketCount totalTickets, + t.lineCount, + IFNULL(ec.errors,0) + IFNULL(ec2.errors,0) errors, + v.volume volume, + SUBSTRING(tc.tableTimed, 1, 5) hourStart, + SUBSTRING(tc.tableTimed, LENGTH(tc.tableTimed)-4, 5) hourEnd, + IFNULL(CAST(tc.timeWorkDecimal AS DECIMAL (10,2)) , 0) hourWorked, + vDatedFrom dated + FROM tmp.total t + LEFT JOIN worker w ON w.id = t.workerFk + LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = t.workerFk + LEFT JOIN tmp.errorsByClaim ec ON ec.workerFk = t.workerFk + LEFT JOIN tmp.volume v ON v.workerFk = t.workerFk + LEFT JOIN tmp.errorsByChecker ec2 ON ec2.workerFk = t.workerFk + JOIN (SELECT DISTINCT w.id -- Verificamos que son sacadores + FROM vn.collection c + JOIN vn.state s ON s.id = c.stateFk + JOIN vn.train tn ON tn.id = c.trainFk + JOIN vn.worker w ON w.id = c.workerFk + WHERE c.created BETWEEN vDatedFrom AND vDatedTo) sub ON sub.id = w.id + GROUP BY w.id; + + CREATE OR REPLACE TEMPORARY TABLE itemPickerErrors -- Errores de los sacadores, derivadores de los revisadores + ENGINE = MEMORY + SELECT COUNT(c.ticketFk) errors, + tt.userFk + FROM claimDevelopment cd + JOIN claim c ON cd.claimFk = c.id + JOIN ticket t ON c.ticketFk = t.id + JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id + JOIN ticketTracking tt ON tt.ticketFk = t.id + JOIN `state` s ON s.id = tt.stateFk + WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo + AND cr.code = 'chk' + AND s.code = 'ON_PREPARATION' + GROUP BY tt.userFk; + + UPDATE productionError ep + JOIN itemPickerErrors ipe ON ipe.workerFk = ep.userFk + SET ep.error = ep.error + ipe.errors + WHERE vDatedFrom = ep.dated AND ep.rol = 'Sacadores'; + + DROP TEMPORARY TABLE itemPickerErrors; + + -- Rellena la tabla vn.productionError con revisores + CALL productionError_addCheckerPackager(vDatedFrom, vDatedTo, "Revisadores"); + + -- Genera la tabla tmp.total para encajadores + CREATE OR REPLACE TEMPORARY TABLE tmp.total + ENGINE = MEMORY + SELECT e.workerFk, + COUNT(DISTINCT t.id) ticketCount, + COUNT(s.id) lineCount + FROM expedition e + JOIN ticket t ON e.ticketFk = t.id + JOIN sale s ON s.ticketFk = t.id + WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo + GROUP BY e.workerFk; + + -- Rellena la tabla vn.productionError con encajadores + CALL productionError_addCheckerPackager(vDatedFrom, vDatedTo, "Encajadores"); + + DROP TEMPORARY TABLE tmp.errorsByClaim, + tmp.volume, + tmp.errorsByChecker, + tmp.expeditionErrors, + tmp.total; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sectorProductivity_add`() +BEGIN + DECLARE vDatedFrom DATETIME; + DECLARE vDatedTo DATETIME; + + SELECT DATE_SUB(util.VN_CURDATE(),INTERVAL 1 DAY), CONCAT(DATE_SUB(util.VN_CURDATE(),INTERVAL 1 DAY),' 23:59:59') INTO vDatedFrom, vDatedTo; + + DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate; + DROP TEMPORARY TABLE IF EXISTS tmp.errorsByChecker; + DROP TEMPORARY TABLE IF EXISTS tmp.previousErrors; + + CALL timeControl_calculateAll(vDatedFrom, vDatedTo); + + CREATE TEMPORARY TABLE tmp.errorsByChecker + ENGINE = MEMORY + SELECT sc.userFk workerFk, COUNT(DISTINCT s.ticketFk) errorsByChecker + FROM saleMistake sm + JOIN vn.saleGroupDetail sgd on sgd.saleFk = sm.saleFk + JOIN vn.sectorCollectionSaleGroup scsg on scsg.saleGroupFk = sgd.saleGroupFk + JOIN vn.sectorCollection sc on sc.id = scsg.sectorCollectionFk + JOIN sale s ON s.id = sm.saleFk + JOIN ticket t on t.id = s.ticketFk + WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo) + GROUP BY sc.userFk ; + + CREATE TEMPORARY TABLE tmp.previousErrors -- Errores de previa, derivadores de los revisadores (por reclamación) + ENGINE = MEMORY + SELECT tt.userFk, COUNT(c.ticketFk) errorsByClaim + FROM claimDevelopment cd + JOIN claim c ON cd.claimFk = c.id + JOIN ticket t ON c.ticketFk = t.id + JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id + JOIN ticketTracking tt ON tt.ticketFk = t.id + JOIN `state` s ON s.id = tt.stateFk + WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo AND cr.description = 'Revisadores' AND s.code = 'OK PREVIOUS' + GROUP BY cd.workerFk; + + DELETE FROM sectorProductivity + WHERE dated = vDatedFrom + AND sector IN ('Algemesi Artificial','Algemesi Complementos'); + + INSERT INTO sectorProductivity(workerFk, firstName, lastName, sector, ticketCount, saleCount, error, volume, hourWorked, dated) + SELECT w.id workerFk, + w.firstName, + w.lastName, + se.description sector, + COUNT(DISTINCT s.ticketFk) ticketCount, + COUNT(sgd.id) saleCount, + IFNULL(ec2.errorsByChecker,0) + IFNULL(pe.errorsByClaim, 0) errors, + wp.volume, + IFNULL(CAST(tc.timeWorkDecimal AS DECIMAL (10,2)) , 0) AS hourWorked, + DATE(vDatedFrom) dated + FROM vn.saleGroupDetail sgd + JOIN vn.saleGroup sg on sg.id = sgd.saleGroupFk + JOIN vn.sectorCollectionSaleGroup scsg on scsg.saleGroupFk = sgd.saleGroupFk + JOIN vn.sectorCollection sc on sc.id = scsg.sectorCollectionFk + join vn.sector se on se.id = sc.sectorFk + JOIN vn.worker w ON w.id = sc.userFk + LEFT JOIN vn.sale s ON s.id = sgd.saleFk + LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = w.id + LEFT JOIN bs.workerProductivity wp ON wp.workerFk = w.id + LEFT JOIN `state` s2 ON s2.id = wp.stateFk AND s2.code = 'OK PREVIOUS' + LEFT JOIN tmp.errorsByChecker ec2 ON ec2.workerFk = w.id + LEFT JOIN tmp.previousErrors pe ON pe.workerFk = w.id + WHERE DATE(sc.created) = vDatedFrom + AND wp.dated = vDatedFrom + GROUP BY w.id; + + DROP TEMPORARY TABLE tmp.timeControlCalculate; + DROP TEMPORARY TABLE tmp.errorsByChecker; + DROP TEMPORARY TABLE tmp.previousErrors; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketStateUpdate`(vTicketFk INT, vStateCode VARCHAR(45)) +BEGIN + + /* + * @deprecated:utilizar ticket_setState + */ + + DECLARE vAlertLevel INT; + + SELECT s.alertLevel INTO vAlertLevel + FROM vn.state s + JOIN vn.ticketState ts ON ts.stateFk = s.id + WHERE ts.ticketFk = vTicketFk; + + IF !(vStateCode = 'ON_CHECKING' AND vAlertLevel > 1) THEN + + INSERT INTO ticketTracking(stateFk, ticketFk, userFk) + SELECT id, vTicketFk, account.myUser_getId() + FROM vn.state + WHERE `code` = vStateCode collate utf8_unicode_ci; + + END IF; + +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_Clone`(vOriginalTicket INT, OUT vNewTicket INT) +BEGIN +/** + * Clona el contenido de un ticket en otro + * + * @param vOriginalTicket ticket Original + * @param vNewTicket ticket creado + */ + DECLARE vStateFk INT; + + INSERT INTO ticket ( + clientFk, + shipped, + addressFk, + agencyModeFk, + nickname, + warehouseFk, + companyFk, + landed, + zoneFk, + zonePrice, + zoneBonus, + routeFk, + priority, + hasPriority, + clonedFrom + ) + SELECT + clientFk, + shipped, + addressFk, + agencyModeFk, + nickname, + warehouseFk, + companyFk, + landed, + zoneFk, + zonePrice, + zoneBonus, + routeFk, + priority, + hasPriority, + vOriginalTicket + FROM ticket + WHERE id = vOriginalTicket; + + SET vNewTicket = LAST_INSERT_ID(); + + INSERT INTO ticketObservation(ticketFk, observationTypeFk, description) + SELECT vNewTicket, observationTypeFk, description + FROM ticketObservation + WHERE ticketFk = vOriginalTicket; + + INSERT INTO ticketTracking(ticketFk, stateFk, userFk, created) + SELECT vNewTicket, stateFk, userFk, created + FROM ticketTracking + WHERE ticketFk = vOriginalTicket + ORDER BY created; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_add`( + vClientId INT + ,vShipped DATE + ,vWarehouseFk INT + ,vCompanyFk INT + ,vAddressFk INT + ,vAgencyModeFk INT + ,vRouteFk INT + ,vlanded DATE + ,vUserId INT + ,vIsRequiredZone INT + ,OUT vNewTicket INT) +BEGIN +/** +* Crea un ticket, +* ¡¡NO se debe llamar directamente, llamar a salix que hace comprobaciones previas!! +* +* @param vClientId id del cliente +* @param vShipped dia preparacion +* @param vWarehouseFk id del warehouse +* @param vCompanyFk id la empresa +* @param vAddressFk id del consignatario +* @param vAgencyModeFk id de la agencia +* @param vRouteFk id de la ruta | NULL +* @param vlanded dia llegada +* @param vUserId que crea el ticket +* @param vIsRequiredZone Indica si tiene que tener zona valida para ser creado +* @return vNewTicket id del ticket creado +*/ + DECLARE vZoneFk INT; + DECLARE vPrice DECIMAL(10,2); + DECLARE vBonus DECIMAL(10,2); + DECLARE vIsActive BOOL; + + IF vClientId IS NULL THEN + CALL util.throw ('CLIENT_NOT_ESPECIFIED'); + END IF; + + SELECT isActive INTO vIsActive + FROM vn.client + WHERE id = vClientId; + + IF NOT vIsActive THEN + CALL util.throw ('CLIENT_NOT_ACTIVE'); + END IF; + + IF NOT vAddressFk OR vAddressFk IS NULL THEN + SELECT id INTO vAddressFk + FROM address + WHERE clientFk = vClientId + AND isDefaultAddress; + END IF; + + IF vAgencyModeFk IS NOT NULL THEN + CALL vn.zone_getShipped (vlanded, vAddressFk, vAgencyModeFk, TRUE); + + SELECT zoneFk, price, bonus + INTO vZoneFk, vPrice, vBonus + FROM tmp.zoneGetShipped + WHERE shipped = vShipped + AND warehouseFk = vWarehouseFk + LIMIT 1; + + IF (vZoneFk IS NULL OR vZoneFk = 0) AND vIsRequiredZone THEN + CALL util.throw ('NOT_ZONE_WITH_THIS_PARAMETERS'); + END IF; + END IF; + + INSERT INTO ticket ( + clientFk, + shipped, + addressFk, + agencyModeFk, + nickname, + warehouseFk, + routeFk, + companyFk, + landed, + zoneFk, + zonePrice, + zoneBonus + ) + SELECT vClientId, + vShipped, + a.id, + vAgencyModeFk, + a.nickname, + vWarehouseFk, + IF(vRouteFk,vRouteFk,NULL), + vCompanyFk, + vlanded, + vZoneFk, + vPrice, + vBonus + FROM address a + JOIN agencyMode am ON am.id = a.agencyModeFk + WHERE a.id = vAddressFk; + + SET vNewTicket = LAST_INSERT_ID(); + + INSERT INTO ticketObservation(ticketFk, observationTypeFk, description) + SELECT vNewTicket, ao.observationTypeFk, ao.description + FROM addressObservation ao + JOIN address a ON a.id = ao.addressFk + WHERE a.id = vAddressFk; + + IF (SELECT COUNT(*) + FROM bs.clientNewBorn cnb + WHERE cnb.clientFk = vClientId + AND NOT cnb.isRookie) = 0 THEN + + CALL vn.ticketObservation_addNewBorn(vNewTicket); + END IF; + + IF (SELECT ct.isCreatedAsServed FROM vn.clientType ct JOIN vn.client c ON c.typeFk = ct.code WHERE c.id = vClientId ) <> FALSE THEN + INSERT INTO ticketTracking(stateFk, ticketFk, userFk) + SELECT id, vNewTicket, account.myUser_getId() + FROM state + WHERE `code` = 'DELIVERED'; + END IF; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setNextState`(vSelf INT) +BEGIN +/** + * Cambia el estado del ticket al siguiente estado según la tabla state + * + * @param vSelf id dle ticket + */ + DECLARE vStateFk INT; + DECLARE vNewStateFk INT; + + SELECT stateFk INTO vStateFk + FROM ticketState + WHERE ticketFk = vSelf; + + SELECT nextStateFk INTO vNewStateFk + FROM state + WHERE id = vStateFk; + + INSERT INTO ticketTracking(stateFk, ticketFk, userFk) + VALUES (vNewStateFk, vSelf, account.myUser_getId()); +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setPreviousState`(vTicketFk INT) +BEGIN + DECLARE vControlFk INT; + + SELECT MAX(id) INTO vControlFk + FROM ticketTracking + WHERE ticketFk = vTicketFk; + + IF (SELECT s.code + FROM vn.state s + JOIN ticketTracking tt ON tt.stateFk = s.id + WHERE tt.id = vControlFk) + = 'PREVIOUS_PREPARATION' THEN + SELECT id + INTO vControlFk + FROM ticketTracking tt + JOIN vn.state s ON tt.stateFk = s.id + WHERE ticketFk = vTicketFk + AND id < vControlFk + AND s.code != 'PREVIOUS_PREPARATION' + ORDER BY id DESC + LIMIT 1; + + INSERT INTO ticketTracking(stateFk, ticketFk, userFk) + SELECT s.nextStateFk, tt.ticketFk, account.myUser_getId() + FROM ticketTracking tt + JOIN vn.state s ON tt.stateFk = s.id + WHERE id = vControlFk; + END IF; +END$$ +DELIMITER ; + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setState`( + vSelf INT, + vStateCode VARCHAR(255) COLLATE utf8_general_ci +) +BEGIN +/** + * Modifica el estado de un ticket si se cumplen las condiciones necesarias. + * + * @param vSelf el id del ticket + * @param vStateCode estado a modificar del ticket + */ + DECLARE vticketAlertLevel INT; + DECLARE vTicketStateCode VARCHAR(255); + DECLARE vCanChangeState BOOL; + DECLARE vPackedAlertLevel INT; + DECLARE vZoneFk INT; + + SELECT s.alertLevel, s.`code`, t.zoneFk + INTO vticketAlertLevel, vTicketStateCode, vZoneFk + FROM state s + JOIN ticketTracking tt ON tt.stateFk = s.id + JOIN ticket t ON t.id = tt.ticketFk + WHERE tt.ticketFk = vSelf + ORDER BY tt.created DESC + LIMIT 1; + + SELECT id INTO vPackedAlertLevel FROM alertLevel WHERE code = 'PACKED'; + + IF vStateCode = 'OK' AND vZoneFk IS NULL THEN + CALL util.throw('ASSIGN_ZONE_FIRST'); + END IF; + + SET vCanChangeState = ( + vStateCode <> 'ON_CHECKING' OR + vticketAlertLevel < vPackedAlertLevel + )AND NOT ( + vTicketStateCode IN ('CHECKED', 'CHECKING') + AND vStateCode IN ('PREPARED', 'ON_PREPARATION') + ); + + IF vCanChangeState THEN + INSERT INTO ticketTracking (stateFk, ticketFk, userFk) + SELECT id, vSelf, account.myUser_getId() + FROM state + WHERE `code` = vStateCode COLLATE utf8_unicode_ci; + + IF vStateCode = 'PACKED' THEN + CALL ticket_doCmr(vSelf); + END IF; + ELSE + CALL util.throw('INCORRECT_TICKET_STATE'); + END IF; +END$$ +DELIMITER ; diff --git a/db/changes/235201/02-views.sql b/db/changes/235201/02-views.sql new file mode 100644 index 000000000..a0f41594d --- /dev/null +++ b/db/changes/235201/02-views.sql @@ -0,0 +1,52 @@ +CREATE OR REPLACE DEFINER=`root`@`localhost` + SQL SECURITY DEFINER + VIEW `vn`.`ticketState` +AS SELECT `tt`.`created` AS `updated`, + `tt`.`stateFk` AS `stateFk`, + `tt`.`userFk` AS `userFk`, + `tls`.`ticketFk` AS `ticketFk`, + `s`.`id` AS `state`, + `s`.`order` AS `productionOrder`, + `s`.`alertLevel` AS `alertLevel`, + `s`.`code` AS `code`, + `tls`.`ticketFk` AS `ticket`, + `tt`.`userFk` AS `worker`, + `s`.`isPreviousPreparable` AS `isPreviousPreparable`, + `s`.`isPicked` AS `isPicked` +FROM ( + ( + `vn`.`ticketLastState` `tls` + JOIN `vn`.`ticketTracking` `tt` ON(`tt`.`id` = `tls`.`ticketTrackingFk`) + ) + JOIN `vn`.`state` `s` ON(`s`.`id` = `tt`.`stateFk`) + ); + +CREATE OR REPLACE DEFINER=`root`@`localhost` + SQL SECURITY DEFINER + VIEW `vn2008`.`v_inter` +AS SELECT `tt`.`id` AS `inter_id`, + `tt`.`stateFk` AS `state_id`, + `tt`.`notes` AS `nota`, + `tt`.`created` AS `odbc_date`, + `tt`.`ticketFk` AS `Id_Ticket`, + `tt`.`userFk` AS `Id_Trabajador`, + `tt`.`supervisorFk` AS `Id_supervisor` +FROM `vn`.`ticketTracking` `tt`; + +CREATE OR REPLACE +ALGORITHM = UNDEFINED VIEW `ticketStateToday` AS +SELECT + `ts`.`ticket` AS `ticket`, + `ts`.`state` AS `state`, + `ts`.`productionOrder` AS `productionOrder`, + `ts`.`alertLevel` AS `alertLevel`, + `ts`.`userFk` AS `userFk`, + `ts`.`code` AS `code`, + `ts`.`updated` AS `updated`, + `ts`.`isPicked` AS `isPicked` +FROM + (`ticketState` `ts` +JOIN `ticket` `t` ON + (`t`.`id` = `ts`.`ticket`)) +WHERE + `t`.`shipped` BETWEEN `util`.`VN_CURDATE`() AND `MIDNIGHT`(`util`.`VN_CURDATE`()); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index f422dcb45..c94433e61 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -5,10 +5,6 @@ SET DEFAULT ROLE 'salix' FOR 'root'@'%'; CREATE SCHEMA IF NOT EXISTS `vn2008`; CREATE SCHEMA IF NOT EXISTS `tmp`; -CREATE ROLE 'salix'; -GRANT 'salix' TO 'root'@'%'; -SET DEFAULT ROLE 'salix' FOR 'root'@'%'; - UPDATE `util`.`config` SET `environment`= 'development'; @@ -497,7 +493,7 @@ INSERT INTO `vn`.`clientCredit`(`clientFk`, `workerFk`, `amount`, `created`) (1104, 9, 90 , util.VN_CURDATE()), (1105, 9, 90 , util.VN_CURDATE()); -INSERT INTO `vn`.`clientCreditLimit`(`id`, `maxAmount`, `roleFk`) +INSERT INTO `vn`.`roleCreditLimit`(`id`, `maxAmount`, `roleFk`) VALUES (1, 9999999, 20), (2, 10000, 21), @@ -775,7 +771,7 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des -- FIX for state hours on local, inter_afterInsert -- UPDATE vncontrol.inter SET odbc_date = DATE_ADD(util.VN_CURDATE(), INTERVAL -10 SECOND); -INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `workerFk`, `created`) +INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `userFk`, `created`) VALUES (1, 16, 5 , DATE_ADD(util.VN_NOW(), INTERVAL -1 MONTH)), (2, 16, 5 , DATE_ADD(util.VN_NOW(), INTERVAL -1 MONTH)), @@ -819,6 +815,7 @@ INSERT INTO `vn`.`config`(`id`, `mdbServer`, `fakeEmail`, `defaultersMaxAmount`, VALUES (1, 'beta-server', 'nightmare@mydomain.com', '200', DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH)); + INSERT INTO `vn`.`greugeType`(`id`, `name`, `code`) VALUES (1, 'Diff', 'diff'), diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml index bed41c63b..8ed58a4e4 100644 --- a/front/salix/locale/es.yml +++ b/front/salix/locale/es.yml @@ -18,6 +18,7 @@ Show summary: Mostrar vista previa What is new: Novedades de la versión Settings: Ajustes There is a new version, click here to reload: Hay una nueva versión, pulse aquí para recargar +This ticket is locked.: Este ticket está bloqueado # Actions diff --git a/front/salix/module.js b/front/salix/module.js index f8fa0faf0..62d6cac98 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -120,7 +120,7 @@ function $exceptionHandler(vnApp, $window, $state, $injector) { messageT = 'Invalid login'; break; case 403: - messageT = 'Access denied'; + messageT = exception.data?.error?.message || 'Access denied'; break; case 502: messageT = 'It seems that the server has fall down'; diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js index be3baccd7..a01590f58 100644 --- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js +++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js @@ -123,7 +123,7 @@ module.exports = Self => { await models.TicketTracking.create({ ticketFk: newRefundTicket.id, stateFk: state.id, - workerFk: worker.id + userFk: worker.id }, myOptions); const salesToRefund = await models.ClaimBeginning.find(salesFilter, myOptions); diff --git a/modules/client/back/model-config.json b/modules/client/back/model-config.json index 0cc5df9a2..a21407732 100644 --- a/modules/client/back/model-config.json +++ b/modules/client/back/model-config.json @@ -29,7 +29,7 @@ "ClientCredit": { "dataSource": "vn" }, - "ClientCreditLimit": { + "RoleCreditLimit": { "dataSource": "vn" }, "ClientConsumptionQueue": { diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 72b702779..a9e14effa 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -463,7 +463,7 @@ module.exports = Self => { throw new UserError(`You can't change the credit set to zero from a financialBoss`); } - const creditLimits = await models.ClientCreditLimit.find({ + const creditLimits = await models.RoleCreditLimit.find({ fields: ['roleFk'], where: { maxAmount: {gte: changes.credit} diff --git a/modules/client/back/models/client-credit-limit.json b/modules/client/back/models/role-credit-limit.json similarity index 85% rename from modules/client/back/models/client-credit-limit.json rename to modules/client/back/models/role-credit-limit.json index 740f0cf53..4ea28b1a4 100644 --- a/modules/client/back/models/client-credit-limit.json +++ b/modules/client/back/models/role-credit-limit.json @@ -1,9 +1,9 @@ { - "name": "ClientCreditLimit", + "name": "RoleCreditLimit", "base": "VnModel", "options": { "mysql": { - "table": "clientCreditLimit" + "table": "roleCreditLimit" } }, "properties": { diff --git a/modules/route/back/methods/route/filter.js b/modules/route/back/methods/route/filter.js index afefa77d1..7c2225dc9 100644 --- a/modules/route/back/methods/route/filter.js +++ b/modules/route/back/methods/route/filter.js @@ -130,13 +130,15 @@ module.exports = Self => { am.name agencyName, u.name AS workerUserName, v.numberPlate AS vehiclePlateNumber, - Date_format(r.time, '%H:%i') hour + Date_format(r.time, '%H:%i') hour, + eu.email FROM route r LEFT JOIN agencyMode am ON am.id = r.agencyModeFk LEFT JOIN agency a ON a.id = am.agencyFk LEFT JOIN vehicle v ON v.id = r.vehicleFk LEFT JOIN worker w ON w.id = r.workerFk - LEFT JOIN account.user u ON u.id = w.id` + LEFT JOIN account.user u ON u.id = w.id + LEFT JOIN account.emailUser eu ON eu.userFk = r.workerFk` ); stmt.merge(conn.makeSuffix(filter)); diff --git a/modules/route/back/methods/route/getExpeditionSummary.js b/modules/route/back/methods/route/getExpeditionSummary.js new file mode 100644 index 000000000..ee89401a8 --- /dev/null +++ b/modules/route/back/methods/route/getExpeditionSummary.js @@ -0,0 +1,64 @@ +module.exports = Self => { + Self.remoteMethod('getExpeditionSummary', { + description: 'Get summary of expeditions for a given route', + accepts: [ + { + arg: 'routeFk', + type: 'number', + required: true, + description: 'Foreign key for Route' + } + ], + returns: { + type: 'object', + root: true + }, + http: { + path: '/getExpeditionSummary', + verb: 'get' + } + }); + + Self.getExpeditionSummary = async(routeFk, options) => { + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const query = ` + SELECT routeFk, + addressFk, + SUM(total) total, + SUM(delivery) delivery, + SUM(lost) lost, + SUM(delivered) delivered, + GROUP_CONCAT(totalPacking ORDER BY total DESC SEPARATOR ' ') itemPackingType + FROM ( + SELECT r.id AS routeFk, + t.addressFk, + CONCAT (IFNULL(e.itemPackingTypeFk,'-'), '', COUNT(*)) totalPacking, + COUNT(*) total, + SUM(est.code = 'ON DELIVERY') delivery, + SUM(est.code = 'LOST') lost, + SUM(est.code = 'DELIVERED') delivered, + t.priority + FROM vn.ticket t + JOIN vn.route r ON r.id = t.routeFk + JOIN vn.expedition e ON e.ticketFk = t.id + LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk + JOIN vn.agencyMode am ON am.id = r.agencyModeFk + JOIN vn.agency ag ON ag.id = am.agencyFk + LEFT JOIN vn.userConfig uc ON uc.userFk = account.myUser_getId() + WHERE (r.created = util.VN_CURDATE() OR r.created = util.yesterday()) + AND t.routeFk = ? + GROUP BY t.addressFk, e.itemPackingTypeFk + ) sub + GROUP BY addressFk + ORDER BY priority DESC + `; + + const results = await Self.rawSql(query, [routeFk], myOptions); + return results; + }; +}; + diff --git a/modules/route/back/methods/route/getTickets.js b/modules/route/back/methods/route/getTickets.js index 1eb9e27f5..d1ebf9ee7 100644 --- a/modules/route/back/methods/route/getTickets.js +++ b/modules/route/back/methods/route/getTickets.js @@ -3,7 +3,7 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; module.exports = Self => { Self.remoteMethod('getTickets', { - description: 'Return the tickets information displayed on the route module', + description: 'Find all instances of the model matched by filter from the data source.', accessType: 'READ', accepts: [ { @@ -40,22 +40,32 @@ module.exports = Self => { t.clientFk, t.priority, t.addressFk, - st.code AS ticketStateCode, - st.name AS ticketStateName, - wh.name AS warehouseName, - tob.description AS ticketObservation, + st.code ticketStateCode, + st.name ticketStateName, + wh.name warehouseName, + tob.description ticketObservation, a.street, a.postalCode, a.city, - am.name AS agencyModeName, - u.nickname AS userNickname, - vn.ticketTotalVolume(t.id) AS volume, + am.name agencyModeName, + u.nickname userNickname, + vn.ticketTotalVolume(t.id) volume, tob.description, - GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt + GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt, + c.phone clientPhone, + c.mobile clientMobile, + a.phone addressPhone, + a.mobile addressMobile, + a.longitude, + a.latitude, + wm.mediaValue salePersonPhone, + t.cmrFk, + t.isSigned signed FROM vn.route r JOIN ticket t ON t.routeFk = r.id - JOIN vn.sale s ON s.ticketFk = t.id - JOIN vn.item i ON i.id = s.itemFk + JOIN client c ON t.clientFk = c.id + LEFT JOIN vn.sale s ON s.ticketFk = t.id + LEFT JOIN vn.item i ON i.id = s.itemFk LEFT JOIN ticketState ts ON ts.ticketFk = t.id LEFT JOIN state st ON st.id = ts.stateFk LEFT JOIN warehouse wh ON wh.id = t.warehouseFk @@ -65,7 +75,8 @@ module.exports = Self => { LEFT JOIN address a ON a.id = t.addressFk LEFT JOIN agencyMode am ON am.id = t.agencyModeFk LEFT JOIN account.user u ON u.id = r.workerFk - LEFT JOIN vehicle v ON v.id = r.vehicleFk` + LEFT JOIN vehicle v ON v.id = r.vehicleFk + LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk` ); if (!filter.where) filter.where = {}; diff --git a/modules/route/back/methods/route/specs/getExpeditionSummary.spec.js b/modules/route/back/methods/route/specs/getExpeditionSummary.spec.js new file mode 100644 index 000000000..9d70c339a --- /dev/null +++ b/modules/route/back/methods/route/specs/getExpeditionSummary.spec.js @@ -0,0 +1,10 @@ +const app = require('vn-loopback/server/server'); + +describe('route getExpeditionSummary()', () => { + const routeId = 1; + it('should return a summary of expeditions for a route', async() => { + const result = await app.models.Route.getExpeditionSummary(routeId); + + expect(result.every(route => route.id = routeId)).toBeTruthy(); + }); +}); diff --git a/modules/route/back/models/route.js b/modules/route/back/models/route.js index cbdd75679..9b5f3564f 100644 --- a/modules/route/back/models/route.js +++ b/modules/route/back/models/route.js @@ -17,6 +17,7 @@ module.exports = Self => { require('../methods/route/cmr')(Self); require('../methods/route/getExternalCmrs')(Self); require('../methods/route/downloadCmrsZip')(Self); + require('../methods/route/getExpeditionSummary')(Self); require('../methods/route/getByWorker')(Self); Self.validate('kmStart', validateDistance, { diff --git a/modules/ticket/back/methods/ticket/specs/state.spec.js b/modules/ticket/back/methods/ticket/specs/state.spec.js index 9b5e80165..f369932de 100644 --- a/modules/ticket/back/methods/ticket/specs/state.spec.js +++ b/modules/ticket/back/methods/ticket/specs/state.spec.js @@ -94,10 +94,10 @@ describe('ticket state()', () => { const ticketTracking = await models.Ticket.state(ctx, params, options); - expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk); - expect(ticketTracking.__data.stateFk).toBe(params.stateFk); - expect(ticketTracking.__data.workerFk).toBe(49); - expect(ticketTracking.__data.id).toBeDefined(); + expect(ticketTracking.ticketFk).toBe(params.ticketFk); + expect(ticketTracking.stateFk).toBe(params.stateFk); + expect(ticketTracking.userFk).toBe(49); + expect(ticketTracking.id).toBeDefined(); await tx.rollback(); } catch (e) { @@ -116,14 +116,14 @@ describe('ticket state()', () => { const ticket = await models.Ticket.create(sampleTicket, options); const ctx = {req: {accessToken: {userId: 18}}}; const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options); - const params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1}; + const params = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1}; const res = await models.Ticket.state(ctx, params, options); - expect(res.__data.ticketFk).toBe(params.ticketFk); - expect(res.__data.stateFk).toBe(params.stateFk); - expect(res.__data.workerFk).toBe(params.workerFk); - expect(res.__data.workerFk).toBe(1); - expect(res.__data.id).toBeDefined(); + expect(res.ticketFk).toBe(params.ticketFk); + expect(res.stateFk).toBe(params.stateFk); + expect(res.userFk).toBe(params.userFk); + expect(res.userFk).toBe(1); + expect(res.id).toBeDefined(); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/ticket/state.js b/modules/ticket/back/methods/ticket/state.js index 01bfbba20..adac2e42f 100644 --- a/modules/ticket/back/methods/ticket/state.js +++ b/modules/ticket/back/methods/ticket/state.js @@ -51,12 +51,12 @@ module.exports = Self => { params.stateFk = state.id; } - if (!params.workerFk) { + if (!params.userFk) { const worker = await models.Worker.findOne({ where: {id: userId} }, myOptions); - params.workerFk = worker.id; + params.userFk = worker.id; } const ticketState = await models.TicketState.findById(params.ticketFk, { diff --git a/modules/ticket/back/models/expedition-state.json b/modules/ticket/back/models/expedition-state.json index 262eb2e38..eda0f79fd 100644 --- a/modules/ticket/back/models/expedition-state.json +++ b/modules/ticket/back/models/expedition-state.json @@ -24,5 +24,12 @@ "userFk": { "type": "number" } + }, + "relations": { + "expeditionStateType": { + "type": "belongsTo", + "model": "ExpeditionStateType", + "foreignKey": "typeFk" + } } } diff --git a/modules/ticket/back/models/ticket-state.json b/modules/ticket/back/models/ticket-state.json index a10938ef0..3ee50fac0 100644 --- a/modules/ticket/back/models/ticket-state.json +++ b/modules/ticket/back/models/ticket-state.json @@ -33,10 +33,10 @@ "model": "State", "foreignKey": "stateFk" }, - "worker": { - "type": "belongsTo", - "model": "Worker", - "foreignKey": "workerFk" - } + "user": { + "type": "belongsTo", + "model": "VnUser", + "foreignKey": "userFk" + } } } diff --git a/modules/ticket/back/models/ticket-tracking.js b/modules/ticket/back/models/ticket-tracking.js index 92e046d3e..72e1880b9 100644 --- a/modules/ticket/back/models/ticket-tracking.js +++ b/modules/ticket/back/models/ticket-tracking.js @@ -2,5 +2,5 @@ module.exports = function(Self) { require('../methods/ticket-tracking/setDelivered')(Self); Self.validatesPresenceOf('stateFk', {message: 'State cannot be blank'}); - Self.validatesPresenceOf('workerFk', {message: 'Worker cannot be blank'}); + Self.validatesPresenceOf('userFk', {message: 'Worker cannot be blank'}); }; diff --git a/modules/ticket/back/models/ticket-tracking.json b/modules/ticket/back/models/ticket-tracking.json index 8b5ce0b64..8b0617145 100644 --- a/modules/ticket/back/models/ticket-tracking.json +++ b/modules/ticket/back/models/ticket-tracking.json @@ -8,21 +8,21 @@ }, "properties": { "id": { - "id": true, - "type": "number", - "forceId": false + "id": true, + "type": "number", + "forceId": false }, "created": { - "type": "date" + "type": "date" }, "ticketFk": { - "type": "number" + "type": "number" }, "stateFk": { - "type": "number" + "type": "number" }, - "workerFk": { - "type": "number" + "userFk": { + "type": "number" } }, "relations": { @@ -36,10 +36,10 @@ "model": "State", "foreignKey": "stateFk" }, - "worker": { - "type": "belongsTo", - "model": "Worker", - "foreignKey": "workerFk" - } + "user": { + "type": "belongsTo", + "model": "VnUser", + "foreignKey": "userFk" + } } } diff --git a/modules/ticket/front/tracking/index/index.js b/modules/ticket/front/tracking/index/index.js index 95665b071..ff3dc881b 100644 --- a/modules/ticket/front/tracking/index/index.js +++ b/modules/ticket/front/tracking/index/index.js @@ -7,15 +7,9 @@ class Controller extends Section { this.filter = { include: [ { - relation: 'worker', + relation: 'user', scope: { - fields: ['id'], - include: { - relation: 'user', - scope: { - fields: ['name'] - } - } + fields: ['name'] } }, { relation: 'state', diff --git a/modules/zone/back/methods/zone/deleteZone.js b/modules/zone/back/methods/zone/deleteZone.js index 13d45428c..38e724cd3 100644 --- a/modules/zone/back/methods/zone/deleteZone.js +++ b/modules/zone/back/methods/zone/deleteZone.js @@ -64,7 +64,7 @@ module.exports = Self => { promises.push(models.TicketTracking.create({ ticketFk: ticket.id, stateFk: fixingState.id, - workerFk: worker.id + userFk: worker.id }, myOptions)); } } diff --git a/print/templates/reports/driver-route/sql/tickets.sql b/print/templates/reports/driver-route/sql/tickets.sql index 09e73819b..9d548c2b3 100644 --- a/print/templates/reports/driver-route/sql/tickets.sql +++ b/print/templates/reports/driver-route/sql/tickets.sql @@ -20,7 +20,7 @@ SELECT u.nickName salesPersonName, ipkg.itemPackingTypes FROM route r - LEFT JOIN ticket t ON t.routeFk = r.id + JOIN ticket t ON t.routeFk = r.id LEFT JOIN address a ON a.id = t.addressFk LEFT JOIN client c ON c.id = t.clientFk LEFT JOIN worker w ON w.id = client_getSalesPerson(t.clientFk, CURDATE()) diff --git a/print/templates/reports/invoice/invoice.html b/print/templates/reports/invoice/invoice.html index 45b6c3934..af1aaa423 100644 --- a/print/templates/reports/invoice/invoice.html +++ b/print/templates/reports/invoice/invoice.html @@ -16,6 +16,7 @@