diff --git a/back/methods/collection/getTickets.js b/back/methods/collection/getTickets.js index 85d1111dfe..dfe86c94c1 100644 --- a/back/methods/collection/getTickets.js +++ b/back/methods/collection/getTickets.js @@ -65,7 +65,8 @@ module.exports = Self => { iss.id itemShelvingSaleFk, iss.isPicked, iss.itemShelvingFk, - st.code stateCode + st.code stateCode, + ac.username FROM ticketCollection tc LEFT JOIN collection c ON c.id = tc.collectionFk JOIN sale s ON s.ticketFk = tc.ticketFk @@ -80,6 +81,7 @@ module.exports = Self => { LEFT JOIN itemColor ic ON ic.itemFk = s.itemFk LEFT JOIN origin o ON o.id = i.originFk LEFT JOIN state st ON st.id = sg.stateFk + LEFT JOIN account.user ac ON ac.id = iss.userFk WHERE tc.collectionFk = ? GROUP BY s.id, ish.id, p.code, p2.code UNION ALL @@ -109,7 +111,8 @@ module.exports = Self => { iss.id itemShelvingSaleFk, iss.isPicked, iss.itemShelvingFk, - st.code stateCode + st.code stateCode, + ac.username FROM sectorCollection sc JOIN sectorCollectionSaleGroup ss ON ss.sectorCollectionFk = sc.id JOIN saleGroup sg ON sg.id = ss.saleGroupFk @@ -124,6 +127,7 @@ module.exports = Self => { LEFT JOIN itemColor ic ON ic.itemFk = s.itemFk LEFT JOIN origin o ON o.id = i.originFk LEFT JOIN state st ON st.id = sg.stateFk + LEFT JOIN account.user ac ON ac.id = sg.userFk WHERE sc.id = ? AND sgd.saleGroupFk GROUP BY s.id, ish.id, p.code, p2.code`, [id, id], myOptions); diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 590fe34b67..30c49aafc9 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -321,6 +321,11 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com' UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23; +INSERT INTO `vn`.`agencyIncoming`(`agencyModeFk`) + VALUES + (1), + (2); + INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`, `hasVerified`) VALUES (1, NULL, 'PayMethod one', 0, 001, 0, 0, 0), diff --git a/db/routines/bs/procedures/ventas_contables_add.sql b/db/routines/bs/procedures/ventas_contables_add.sql index 72b0c0feed..c82cb96d9c 100644 --- a/db/routines/bs/procedures/ventas_contables_add.sql +++ b/db/routines/bs/procedures/ventas_contables_add.sql @@ -15,7 +15,7 @@ BEGIN DELETE FROM bs.ventas_contables WHERE year = vYear - AND month = vMonth; + AND month = vMonth; DROP TEMPORARY TABLE IF EXISTS tmp.ticket_list; CREATE TEMPORARY TABLE tmp.ticket_list diff --git a/db/routines/vn/procedures/entry_clone.sql b/db/routines/vn/procedures/entry_clone.sql index a0ed39c295..511ff4837f 100644 --- a/db/routines/vn/procedures/entry_clone.sql +++ b/db/routines/vn/procedures/entry_clone.sql @@ -1,20 +1,31 @@ DELIMITER $$ -CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_clone`(vSelf INT) +CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_clone`( + vSelf INT, + OUT vOutputEntryFk INT +) BEGIN /** * clones an entry. * * @param vSelf The entry id + * @param vOutputEntryFk The new entry id */ DECLARE vNewEntryFk INT; - START TRANSACTION; + DECLARE vIsRequiredTx BOOL DEFAULT NOT @@in_transaction; + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + CALL util.tx_rollback(vIsRequiredTx); + RESIGNAL; + END; + + CALL util.tx_start(vIsRequiredTx); CALL entry_cloneHeader(vSelf, vNewEntryFk, NULL); CALL entry_copyBuys(vSelf, vNewEntryFk); - COMMIT; + CALL util.tx_commit(vIsRequiredTx); + SET vOutputEntryFk = vNewEntryFk; - SELECT vNewEntryFk; END$$ DELIMITER ; diff --git a/db/routines/vn/procedures/entry_splitByShelving.sql b/db/routines/vn/procedures/entry_splitByShelving.sql index f5de360980..019abe6cb1 100644 --- a/db/routines/vn/procedures/entry_splitByShelving.sql +++ b/db/routines/vn/procedures/entry_splitByShelving.sql @@ -39,14 +39,14 @@ BEGIN read_loop: LOOP SET vDone = FALSE; - + FETCH cur INTO vBuyFk, vIshStickers, vBuyStickers; IF vDone THEN LEAVE read_loop; END IF; - IF vIshStickers = vBuyStickers THEN + IF vIshStickers = vBuyStickers THEN UPDATE buy SET entryFk = vToEntryFk WHERE id = vBuyFk; diff --git a/db/routines/vn/procedures/entry_transfer.sql b/db/routines/vn/procedures/entry_transfer.sql new file mode 100644 index 0000000000..5b83ae5321 --- /dev/null +++ b/db/routines/vn/procedures/entry_transfer.sql @@ -0,0 +1,158 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`entry_transfer`( + vOriginalEntry INT, + OUT vNewEntryFk INT + ) +BEGIN +/** + * Adelanta a mañana la mercancia de una entrada a partir de lo que hay ubicado en el almacén + * + * @param vOriginalEntry entrada que se quiera adelantar + * @param vNewEntry nueva entrada creada + */ + DECLARE vTravelFk INT; + DECLARE vWarehouseFk INT; + DECLARE vWarehouseInFk INT; + DECLARE vWarehouseOutFk INT; + DECLARE vRef INT; + DECLARE vIsReceived INT; + DECLARE vAgencyModeFk INT; + DECLARE vTomorrow DATETIME DEFAULT util.tomorrow(); + DECLARE vCurDate DATE DEFAULT util.VN_CURDATE(); + + DECLARE vIsRequiredTx BOOL DEFAULT NOT @@in_transaction; + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + CALL util.tx_rollback(vIsRequiredTx); + RESIGNAL; + END; + + -- Clonar la entrada + CALL entry_clone(vOriginalEntry, vNewEntryFk); + + CALL util.tx_start(vIsRequiredTx); + + /* Hay que crear un nuevo travel, con salida hoy y llegada mañana y + asignar la entrada nueva al nuevo travel.*/ + SELECT t.warehouseInFk, t.warehouseOutFk, t.`ref`, t.isReceived, t.agencyModeFk + INTO vWarehouseInFk, vWarehouseOutFk, vRef, vIsReceived, vAgencyModeFk + FROM travel t + JOIN entry e ON e.travelFk = t.id + WHERE e.id = vOriginalEntry; + + SELECT id INTO vTravelFk + FROM travel t + WHERE shipped = vCurDate + AND landed = vTomorrow + AND warehouseInFk = vWarehouseInFk + AND warehouseOutFk = vWarehouseOutFk + AND `ref` = vRef + AND isReceived =vIsReceived + AND agencyModeFk = vAgencyModeFk; + + IF vTravelFk IS NULL THEN + INSERT INTO travel( + shipped, + landed, + warehouseInFk, + warehouseOutFk, + `ref`, + isReceived, + agencyModeFk) + SELECT vCurDate, + vTomorrow, + t.warehouseInFk, + t.warehouseOutFk, + t.`ref`, + t.isReceived, + t.agencyModeFk + FROM travel t + JOIN entry e ON e.travelFk = t.id + WHERE e.id = vOriginalEntry; + + SET vTravelFk = LAST_INSERT_ID(); + END IF; + + UPDATE entry + SET travelFk = vTravelFk, + evaNotes = vOriginalEntry + WHERE id = vNewEntryFk; + + -- Poner a 0 las cantidades + UPDATE buy b + SET b.quantity = 0, b.stickers = 0 + WHERE b.entryFk = vNewEntryFk; + + -- Eliminar duplicados + DELETE b + FROM buy b + LEFT JOIN (SELECT b.id, b.itemFk + FROM buy b + WHERE b.entryFk = vNewEntryFk + GROUP BY b.itemFk) tBuy ON tBuy.id = b.id + WHERE b.entryFk = vNewEntryFk + AND tBuy.id IS NULL; + + SELECT t.warehouseInFk INTO vWarehouseFk + FROM travel t + JOIN entry e ON e.travelFk = t.id + WHERE e.id = vOriginalEntry; + + /* Actualizar nueva entrada con lo que no está ubicado HOY, + descontando lo vendido HOY de esas ubicaciones*/ + CREATE OR REPLACE TEMPORARY TABLE buys + WITH tBuy AS ( + SELECT b.itemFk, SUM(b.quantity) totalQuantity + FROM vn.buy b + WHERE b.entryFk = vOriginalEntry + GROUP BY b.itemFk + ), + itemShelvings AS ( + SELECT ish.itemFk, SUM(ish.visible) visible + FROM vn.itemShelving ish + JOIN vn.shelving sh ON sh.id = ish.shelvingFk + JOIN vn.parking p ON p.id = sh.parkingFk + JOIN vn.sector s ON s.id = p.sectorFk + JOIN vn.buy b ON b.id = ish.buyFk + JOIN vn.entry e ON e.id = b.entryFk + JOIN tBuy t ON t.itemFk = ish.itemFk + WHERE s.warehouseFk = vWarehouseFk + AND sh.parked >= vCurDate + GROUP BY ish.itemFk + ), + sales AS ( + SELECT s.itemFk, SUM(s.quantity) sold + FROM vn.ticket t + JOIN vn.sale s ON s.ticketFk = t.id + JOIN vn.itemShelvingSale iss ON iss.saleFk = s.id + JOIN vn.itemShelving is2 ON is2.id = iss.itemShelvingFk + JOIN vn.shelving s2 ON s2.id = is2.shelvingFk + JOIN tBuy t ON t.itemFk = s.itemFk + WHERE t.shipped BETWEEN vCurDate AND util.dayend(vCurDate) + AND s2.parked >= vCurDate + GROUP BY s.itemFk + ) + SELECT tmp.itemFk, + IFNULL(iss.visible, 0) visible, + tmp.totalQuantity, + IFNULL(s.sold, 0) sold + FROM tBuy tmp + LEFT JOIN itemShelvings iss ON tmp.itemFk = iss.itemFk + LEFT JOIN sales s ON s.itemFk = tmp.itemFk + WHERE visible < tmp.totalQuantity + OR iss.itemFk IS NULL; + + UPDATE buy b + JOIN buys tmp ON tmp.itemFk = b.itemFk + SET b.quantity = tmp.totalQuantity - tmp.visible - tmp.sold + WHERE b.entryFk = vNewEntryFk; + + -- Limpia la nueva entrada + DELETE FROM buy WHERE entryFk = vNewEntryFk AND quantity = 0; + + CALL util.tx_commit(vIsRequiredTx); + + CALL cache.visible_refresh(@c,TRUE,vWarehouseFk); + CALL cache.available_refresh(@c, TRUE, vWarehouseFk, vCurDate); +END$$ +DELIMITER ; diff --git a/db/routines/vn/procedures/ticket_doCmr.sql b/db/routines/vn/procedures/ticket_doCmr.sql index ba64944f87..59bb3e0704 100644 --- a/db/routines/vn/procedures/ticket_doCmr.sql +++ b/db/routines/vn/procedures/ticket_doCmr.sql @@ -21,9 +21,6 @@ BEGIN IFNULL(sat.supplierFk, su.id) supplierFk, t.landed FROM ticket t - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN `state` s ON s.id = ts.stateFk - JOIN alertLevel al ON al.id = s.alertLevel JOIN client c ON c.id = t.clientFk JOIN `address` a ON a.id = t.addressFk JOIN province p ON p.id = a.provinceFk @@ -40,8 +37,7 @@ BEGIN LEFT JOIN agency ag ON ag.id = am.agencyFk LEFT JOIN supplierAgencyTerm sat ON sat.agencyFk = ag.id AND wo.isFreelance - WHERE al.code IN ('PACKED', 'DELIVERED') - AND co.code <> 'ES' + WHERE co.code <> 'ES' AND am.name <> 'ABONO' AND w.code = 'ALG' AND t.id = vSelf diff --git a/db/routines/vn/views/agencyModeIncoming.sql b/db/routines/vn/views/agencyModeIncoming.sql new file mode 100644 index 0000000000..f7f6e595df --- /dev/null +++ b/db/routines/vn/views/agencyModeIncoming.sql @@ -0,0 +1,9 @@ +CREATE OR REPLACE DEFINER=`vn`@`localhost` +SQL SECURITY DEFINER +VIEW `vn`.`agencyModeIncoming` AS + SELECT + am.id, + am.name + FROM `vn`.`agencyMode` AS am + JOIN `vn`.`agencyIncoming` AS ai + ON am.id = ai.agencyModeFk; diff --git a/db/versions/11269-wheatBirch/00-firstScript.sql b/db/versions/11269-wheatBirch/00-firstScript.sql index 9432d131b5..9552fe6cd7 100644 --- a/db/versions/11269-wheatBirch/00-firstScript.sql +++ b/db/versions/11269-wheatBirch/00-firstScript.sql @@ -7,9 +7,9 @@ ALTER TABLE vn.invoiceOut ADD CONSTRAINT invoiceOut_customsAgentFk FOREIGN KEY ( ALTER TABLE vn.invoiceOut ADD CONSTRAINT invoiceOut_incotermsFk FOREIGN KEY (incotermsFk) REFERENCES vn.incoterms (`code`) ON DELETE RESTRICT ON UPDATE CASCADE; -UPDATE vn.invoiceOut io - JOIN vn.client c ON c.id = io.clientFk - JOIN vn.ticket t ON t.clientFk = c.id - JOIN vn.address a ON a.id = t.addressFk - SET io.customsAgentFk = a.customsAgentFk, - io.incotermsFk = a.incotermsFk; \ No newline at end of file +-- UPDATE vn.invoiceOut io +-- JOIN vn.client c ON c.id = io.clientFk +-- JOIN vn.ticket t ON t.clientFk = c.id +-- JOIN vn.address a ON a.id = t.addressFk +-- SET io.customsAgentFk = a.customsAgentFk, +-- io.incotermsFk = a.incotermsFk; diff --git a/db/versions/11383-maroonChico/00-town.sql b/db/versions/11383-maroonChico/00-town.sql new file mode 100644 index 0000000000..8fdd8c7b09 --- /dev/null +++ b/db/versions/11383-maroonChico/00-town.sql @@ -0,0 +1,10 @@ +UPDATE vn.town t + LEFT JOIN vn.zoneGeo zg ON zg.id = t.geoFk + SET t.geoFk = NULL + WHERE zg.id IS NULL; + +ALTER TABLE vn.town + ADD CONSTRAINT town_zoneGeo_FK FOREIGN KEY (geoFk) + REFERENCES vn.zoneGeo(id) + ON DELETE RESTRICT + ON UPDATE CASCADE; diff --git a/db/versions/11383-maroonChico/01-postCode.sql b/db/versions/11383-maroonChico/01-postCode.sql new file mode 100644 index 0000000000..668cf69cbf --- /dev/null +++ b/db/versions/11383-maroonChico/01-postCode.sql @@ -0,0 +1,10 @@ +UPDATE vn.postCode pc + LEFT JOIN vn.zoneGeo zg ON zg.id = pc.geoFk + SET pc.geoFk = NULL + WHERE zg.id IS NULL; + +ALTER TABLE vn.postCode + ADD CONSTRAINT postCode_zoneGeo_FK FOREIGN KEY (geoFk) + REFERENCES vn.zoneGeo(id) + ON DELETE RESTRICT + ON UPDATE CASCADE; diff --git a/db/versions/11383-maroonChico/02-province.sql b/db/versions/11383-maroonChico/02-province.sql new file mode 100644 index 0000000000..c16d33cd8d --- /dev/null +++ b/db/versions/11383-maroonChico/02-province.sql @@ -0,0 +1,10 @@ +UPDATE vn.province p + LEFT JOIN vn.zoneGeo zg ON zg.id = p.geoFk + SET p.geoFk = NULL + WHERE zg.id IS NULL; + +ALTER TABLE vn.province + ADD CONSTRAINT province_zoneGeo_FK FOREIGN KEY (geoFk) + REFERENCES vn.zoneGeo(id) + ON DELETE RESTRICT + ON UPDATE CASCADE; diff --git a/db/versions/11411-turquoiseEucalyptus/00-agencyIncomingForeign.sql b/db/versions/11411-turquoiseEucalyptus/00-agencyIncomingForeign.sql new file mode 100644 index 0000000000..829236a2a4 --- /dev/null +++ b/db/versions/11411-turquoiseEucalyptus/00-agencyIncomingForeign.sql @@ -0,0 +1,12 @@ +DELETE ai from + `vn`.`agencyIncoming` ai + LEFT JOIN `vn`.`agencyMode` am ON + am.id = ai.agencyModeFk + WHERE am.id IS null; + +ALTER TABLE `vn`.`agencyIncoming` + ADD CONSTRAINT `fk_agencyIncoming_agencyMode` + FOREIGN KEY (`agencyModeFk`) + REFERENCES `agencyMode`(`id`) + ON DELETE CASCADE + ON UPDATE CASCADE; diff --git a/db/versions/11411-turquoiseEucalyptus/01-travelThermographAlter.sql b/db/versions/11411-turquoiseEucalyptus/01-travelThermographAlter.sql new file mode 100644 index 0000000000..3838f59d79 --- /dev/null +++ b/db/versions/11411-turquoiseEucalyptus/01-travelThermographAlter.sql @@ -0,0 +1,7 @@ +ALTER TABLE `vn`.`travelThermograph` + ADD COLUMN `agencyModeFk` INT(11) NULL AFTER `editorFk`, + ADD CONSTRAINT `travelThermograph_agencyIncoming_fk` + FOREIGN KEY (`agencyModeFk`) + REFERENCES `agencyIncoming`(`agencyModeFk`) + ON DELETE RESTRICT + ON UPDATE CASCADE; diff --git a/db/versions/11419-orangeSalal/00-firstScript.sql b/db/versions/11419-orangeSalal/00-firstScript.sql new file mode 100644 index 0000000000..432ed70aa2 --- /dev/null +++ b/db/versions/11419-orangeSalal/00-firstScript.sql @@ -0,0 +1,2 @@ +ALTER TABLE `vn`.`tag` +ADD COLUMN IF NOT EXISTS `validationRegex` varchar(50) DEFAULT NULL; diff --git a/db/versions/11423-maroonMonstera/00-firstScript.sql b/db/versions/11423-maroonMonstera/00-firstScript.sql new file mode 100644 index 0000000000..cf21473d8c --- /dev/null +++ b/db/versions/11423-maroonMonstera/00-firstScript.sql @@ -0,0 +1 @@ +ALTER TABLE vn.address MODIFY COLUMN isEqualizated tinyint(1) NULL; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index abd2f79a0d..f79dad236e 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -397,4 +397,4 @@ "Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}", "Ticket has been delivered out of order": "El ticket {{ticket}} {{{fullUrl}}} no ha sigo entregado en su orden.", "Price cannot be blank": "El precio no puede estar en blanco" -} \ No newline at end of file +} diff --git a/modules/client/back/methods/client/specs/updateAddress.spec.js b/modules/client/back/methods/client/specs/updateAddress.spec.js index 0453332d71..233ab9ccb8 100644 --- a/modules/client/back/methods/client/specs/updateAddress.spec.js +++ b/modules/client/back/methods/client/specs/updateAddress.spec.js @@ -157,4 +157,52 @@ describe('Address updateAddress', () => { throw e; } }); + + it('should update ticket observations when updateObservations is true', async() => { + const tx = await models.Client.beginTransaction({}); + const client = 1103; + const address = 123; + const ticket = 31; + const observationType = 3; + + const salesAssistantId = 21; + const addressObservation = 'nuevo texto'; + const ticketObservation = 'texto a modificar'; + + try { + const options = {transaction: tx}; + ctx.req.accessToken.userId = salesAssistantId; + ctx.args = { + updateObservations: true, + incotermsFk: incotermsId, + provinceFk: provinceId, + customsAgentFk: customAgentOneId + }; + + await models.AddressObservation.create({ + addressFk: address, + observationTypeFk: observationType, + description: addressObservation + }, options); + + await models.TicketObservation.create({ + ticketFk: ticket, + observationTypeFk: observationType, + description: ticketObservation + }, options); + + await models.Client.updateAddress(ctx, client, address, options); + + const updatedObservation = await models.TicketObservation.findOne({ + where: {ticketFk: ticket, observationTypeFk: observationType} + }, options); + + expect(updatedObservation.description).toEqual(addressObservation); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/client/back/methods/client/updateAddress.js b/modules/client/back/methods/client/updateAddress.js index efef83d6b3..e6e5adb451 100644 --- a/modules/client/back/methods/client/updateAddress.js +++ b/modules/client/back/methods/client/updateAddress.js @@ -80,6 +80,10 @@ module.exports = function(Self) { { arg: 'latitude', type: 'any', + }, + { + arg: 'updateObservations', + type: 'boolean' } ], returns: { @@ -135,6 +139,17 @@ module.exports = function(Self) { delete args.ctx; // Remove unwanted properties const updatedAddress = await address.updateAttributes(ctx.args, myOptions); + if (args.updateObservations) { + const ticket = await Self.rawSql(` + UPDATE ticketObservation to2 + JOIN ticket t ON t.id = to2.ticketFk + JOIN address a ON a.id = t.addressFk + JOIN addressObservation ao ON ao.addressFk = a.id + SET to2.description = ao.description + WHERE ao.observationTypeFk = to2.observationTypeFk + AND a.id = ? + AND t.shipped >= util.VN_CURDATE()`, [addressId], myOptions); + } return updatedAddress; }; diff --git a/modules/client/back/methods/defaulter/filter.js b/modules/client/back/methods/defaulter/filter.js index cf8bd855ab..e00048cf54 100644 --- a/modules/client/back/methods/defaulter/filter.js +++ b/modules/client/back/methods/defaulter/filter.js @@ -21,7 +21,7 @@ module.exports = Self => { } ], returns: { - type: ['object'], + type: 'object', root: true }, http: { @@ -41,23 +41,28 @@ module.exports = Self => { switch (param) { case 'search': return {or: [ - {'d.clientFk': value}, - {'d.clientName': {like: `%${value}%`}} + {'c.id': value}, + {'c.name': {like: `%${value}%`}} ]}; } }); - filter = mergeFilters(ctx.args.filter, {where}); + const date = Date.vnNew(); + date.setHours(0, 0, 0, 0); + + filter = mergeFilters({where: {'d.created': date, 'd.amount': {gt: 0}}}, ctx.args.filter); + filter = mergeFilters(filter, {where}); const stmts = []; - const date = Date.vnNew(); - date.setHours(0, 0, 0, 0); - const stmt = new ParameterizedSQL( - `SELECT * - FROM ( - SELECT - DISTINCT c.id clientFk, + let stmt = new ParameterizedSQL( + `CREATE OR REPLACE TEMPORARY TABLE tmp.defaulters + WITH clientObservations AS + (SELECT clientFk,text, created, workerFk + FROM vn.clientObservation + GROUP BY clientFk + ORDER BY created DESC + )SELECT c.id clientFk, c.name clientName, c.salesPersonFk, c.businessTypeFk = 'worker' isWorker, @@ -80,36 +85,43 @@ module.exports = Self => { JOIN client c ON c.id = d.clientFk JOIN country cn ON cn.id = c.countryFk JOIN payMethod pm ON pm.id = c.payMethodFk - LEFT JOIN clientObservation co ON co.clientFk = c.id + LEFT JOIN clientObservations co ON co.clientFk = c.id LEFT JOIN account.user u ON u.id = c.salesPersonFk LEFT JOIN account.user uw ON uw.id = co.workerFk LEFT JOIN ( - SELECT r1.started, r1.clientFk, r1.finished + SELECT r1.started, r1.clientFk, r1.finished FROM recovery r1 JOIN ( - SELECT MAX(started) AS maxStarted, clientFk + SELECT MAX(started) maxStarted, clientFk FROM recovery GROUP BY clientFk ) r2 ON r1.clientFk = r2.clientFk AND r1.started = r2.maxStarted + WHERE r1.finished + GROUP BY r1.clientFk ) r ON r.clientFk = c.id LEFT JOIN workerDepartment wd ON wd.workerFk = u.id - LEFT JOIN department dp ON dp.id = wd.departmentFk - WHERE - d.created = ? - AND d.amount > 0 - ORDER BY co.created DESC) d` - , [date]); + LEFT JOIN department dp ON dp.id = wd.departmentFk`); stmt.merge(conn.makeWhere(filter.where)); - stmt.merge(`GROUP BY d.clientFk`); + stmts.push(stmt); + + stmt = new ParameterizedSQL(` + SELECT SUM(amount) amount + FROM tmp.defaulters + `); + stmts.push(stmt); + + stmt = new ParameterizedSQL(` + SELECT * + FROM tmp.defaulters + `); stmt.merge(conn.makeOrderBy(filter.order)); - stmt.merge(conn.makeLimit(filter)); const itemsIndex = stmts.push(stmt) - 1; const sql = ParameterizedSQL.join(stmts, ';'); const result = await conn.executeStmt(sql, myOptions); - return itemsIndex === 0 ? result : result[itemsIndex]; + return {defaulters: result[itemsIndex], amount: result[itemsIndex - 1][0].amount}; }; }; diff --git a/modules/client/back/methods/defaulter/specs/filter.spec.js b/modules/client/back/methods/defaulter/specs/filter.spec.js index 0a970823e5..ca7a6b4ffe 100644 --- a/modules/client/back/methods/defaulter/specs/filter.spec.js +++ b/modules/client/back/methods/defaulter/specs/filter.spec.js @@ -11,10 +11,10 @@ describe('defaulter filter()', () => { const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}}; const result = await models.Defaulter.filter(ctx, null, options); - const firstRow = result[0]; + const firstRow = result.defaulters[0]; expect(firstRow.clientFk).toEqual(1101); - expect(result.length).toEqual(5); + expect(result.defaulters.length).toEqual(5); await tx.rollback(); } catch (e) { @@ -31,7 +31,7 @@ describe('defaulter filter()', () => { const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}}; const result = await models.Defaulter.filter(ctx, null, options); - const firstRow = result[0]; + const firstRow = result.defaulters[0]; expect(firstRow.clientFk).toEqual(1101); @@ -50,7 +50,7 @@ describe('defaulter filter()', () => { const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'Petter Parker'}}; const result = await models.Defaulter.filter(ctx, null, options); - const firstRow = result[0]; + const firstRow = result.defaulters[0]; expect(firstRow.clientName).toEqual('Petter Parker'); @@ -60,4 +60,23 @@ describe('defaulter filter()', () => { throw e; } }); + + it('should return the defaulter the sum of every defaulters', async() => { + const tx = await models.Defaulter.beginTransaction({}); + + try { + const options = {transaction: tx}; + const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}}; + const {defaulters, amount} = await models.Defaulter.filter(ctx, null, options); + + const total = defaulters.reduce((total, row) => total + row.amount, 0); + + expect(total).toEqual(amount); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/entry/back/methods/entry/specs/transfer.spec.js b/modules/entry/back/methods/entry/specs/transfer.spec.js new file mode 100644 index 0000000000..bf0b2b974a --- /dev/null +++ b/modules/entry/back/methods/entry/specs/transfer.spec.js @@ -0,0 +1,43 @@ +const models = require('vn-loopback/server/server').models; + +describe('Transfer merchandise from one entry to the next day()', () => { + it('should Transfer buys not located', async() => { + const tx = await models.ItemShelving.beginTransaction({}); + const options = {transaction: tx}; + + try { + const id = 3; + const item = 8; + const buy = 6; + const originalEntry = 4; + const ctx = {req: {accessToken: {userId: 48}}}; + + const currentItemShelving = await models.ItemShelving.findOne({where: {id}}, options); + await currentItemShelving.updateAttributes({itemFk: item, buyFk: buy}, options); + + const {newEntryFk} = await models.Entry.transfer(ctx, originalEntry, options); + const originalEntrybuys = await models.Buy.find({where: {entryFk: originalEntry}}, options); + + const newEntrybuys = await models.Buy.find({where: {entryFk: newEntryFk}}, options); + + const itemShelvingsWithBuys = await models.Buy.find({ + include: { + relation: 'itemShelving', + scope: { + fields: ['id'], + }, + }, + where: {entryFk: originalEntry}, + }, options); + + const hasItemShelving = await itemShelvingsWithBuys.filter(buy => buy.itemShelving().length); + + expect(newEntrybuys.length).toEqual(originalEntrybuys.length - hasItemShelving.length); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/modules/entry/back/methods/entry/transfer.js b/modules/entry/back/methods/entry/transfer.js new file mode 100644 index 0000000000..db68736633 --- /dev/null +++ b/modules/entry/back/methods/entry/transfer.js @@ -0,0 +1,45 @@ +module.exports = Self => { + Self.remoteMethodCtx('transfer', { + description: 'Transfer merchandise from one entry to the next day', + accepts: [ + { + arg: 'id', + type: 'number', + required: true, + http: {source: 'path'} + } + ], + http: { + path: '/:id/transfer', + verb: 'POST' + }, + returns: { + arg: 'newEntryFk', + type: 'number' + } + }); + + Self.transfer = async(ctx, id, options) => { + const myOptions = {userId: ctx.req.accessToken.userId}; + let tx; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + await Self.rawSql('CALL vn.entry_transfer(?, @vNewEntry)', [id], myOptions); + const [newEntryFk] = await Self.rawSql('SELECT @vNewEntry newEntryFk', null, myOptions); + + if (tx) await tx.commit(); + return newEntryFk; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/entry/back/models/buy.json b/modules/entry/back/models/buy.json index 14cafde06b..cd1e54de73 100644 --- a/modules/entry/back/models/buy.json +++ b/modules/entry/back/models/buy.json @@ -114,6 +114,11 @@ "type": "belongsTo", "model": "Delivery", "foreignKey": "deliveryFk" - } + }, + "itemShelving": { + "type": "hasMany", + "model": "ItemShelving", + "foreignKey": "buyFk" + } } } diff --git a/modules/entry/back/models/entry.js b/modules/entry/back/models/entry.js index 55a23bb0a2..03cbd6e7f9 100644 --- a/modules/entry/back/models/entry.js +++ b/modules/entry/back/models/entry.js @@ -12,6 +12,7 @@ module.exports = Self => { require('../methods/entry/addFromPackaging')(Self); require('../methods/entry/addFromBuy')(Self); require('../methods/entry/buyLabel')(Self); + require('../methods/entry/transfer')(Self); require('../methods/entry/labelSupplier')(Self); require('../methods/entry/buyLabelSupplier')(Self); diff --git a/modules/item/back/models/item-shelving.json b/modules/item/back/models/item-shelving.json index 483d6bf3dc..5c31e9e4e1 100644 --- a/modules/item/back/models/item-shelving.json +++ b/modules/item/back/models/item-shelving.json @@ -61,6 +61,11 @@ "type": "belongsTo", "model": "Shelving", "foreignKey": "shelvingFk" - } + }, + "buy": { + "type": "belongsTo", + "model": "Buy", + "foreignKey": "buyFk" + } } -} \ No newline at end of file +} diff --git a/modules/item/back/models/item-tag.js b/modules/item/back/models/item-tag.js index 5b71639130..2cd2e5f9bf 100644 --- a/modules/item/back/models/item-tag.js +++ b/modules/item/back/models/item-tag.js @@ -10,4 +10,27 @@ module.exports = Self => { return new UserError(`Tag value cannot be blank`); return err; }); + + Self.observe('before save', async ctx => { + let tagFk; + let value; + + if (ctx.isNewInstance) { + tagFk = ctx.instance.tagFk; + value = ctx.instance.value; + } else { + tagFk = ctx.currentInstance.tagFk; + value = ctx.data.value; + } + const models = Self.app.models; + const validTag = await models.Tag.findById(tagFk); + + if (validTag.validationRegex) { + const regexString = validTag.validationRegex.replace(/\\\\/g, '\\'); + const validExpresion = new RegExp(regexString); + + if (value && !validExpresion.test(value)) + throw new UserError('The value must be a number or a range of numbers'); + } + }); }; diff --git a/modules/item/back/models/tag.json b/modules/item/back/models/tag.json index 6c5f5c0ba1..5269b849f9 100644 --- a/modules/item/back/models/tag.json +++ b/modules/item/back/models/tag.json @@ -30,6 +30,9 @@ "mysql": { "columnName": "isQuantitatif" } + }, + "validationRegex": { + "type": "string" } }, "acls": [ diff --git a/modules/ticket/back/methods/ticket/specs/transferClient.spec.js b/modules/ticket/back/methods/ticket/specs/transferClient.spec.js index d3ac3c6aa6..063433680b 100644 --- a/modules/ticket/back/methods/ticket/specs/transferClient.spec.js +++ b/modules/ticket/back/methods/ticket/specs/transferClient.spec.js @@ -22,14 +22,16 @@ describe('Ticket transferClient()', () => { it('should throw an error as the ticket is not editable', async() => { try { const ticketId = 4; - await models.Ticket.transferClient(ctx, ticketId, clientId, options); + const addressFk = null; + await models.Ticket.transferClient(ctx, ticketId, clientId, addressFk, options); } catch (e) { expect(e.message).toEqual('This ticket is locked'); } }); it('should be assigned a different clientFk and nickname in the original ticket', async() => { - await models.Ticket.transferClient(ctx, 2, clientId, options); + const addressFk = null; + await models.Ticket.transferClient(ctx, 2, clientId, addressFk, options); const afterTransfer = await models.Ticket.findById(2, null, options); const client = await models.Client.findById(clientId, {fields: ['defaultAddressFk']}, options); const address = await models.Address.findById(client.defaultAddressFk, {fields: ['nickname']}, options); @@ -39,7 +41,8 @@ describe('Ticket transferClient()', () => { }); it('should be assigned a different clientFk and nickname in the original and refund ticket and claim', async() => { - await models.Ticket.transferClient(ctx, originalTicketId, clientId, options); + const addressFk = null; + await models.Ticket.transferClient(ctx, originalTicketId, clientId, addressFk, options); const [originalTicket, refundTicket] = await models.Ticket.find({ where: {id: {inq: [originalTicketId, refundTicketId]}} @@ -59,4 +62,39 @@ describe('Ticket transferClient()', () => { expect(originalTicket.nickname).toEqual(address.nickname); expect(refundTicket.nickname).toEqual(address.nickname); }); + + it('should be assigned a different addressFk and nickname in the original and refund ticket', async() => { + const addressFk = 131; + await models.Ticket.transferClient(ctx, originalTicketId, clientId, addressFk, options); + + const [originalTicket, refundTicket] = await models.Ticket.find({ + where: {id: {inq: [originalTicketId, refundTicketId]}} + }, options); + + const claim = await models.Claim.findOne({ + where: {ticketFk: originalTicketId} + }, options); + + const address = await models.Address.findById(addressFk, {fields: ['id', 'nickname', 'clientFk']}, options); + + expect(originalTicket.clientFk).toEqual(clientId); + expect(originalTicket.clientFk).toEqual(address.clientFk); + expect(refundTicket.clientFk).toEqual(clientId); + expect(refundTicket.clientFk).toEqual(address.clientFk); + expect(claim.clientFk).toEqual(clientId); + expect(claim.clientFk).toEqual(address.clientFk); + + expect(originalTicket.nickname).toEqual(address.nickname); + expect(refundTicket.nickname).toEqual(address.nickname); + }); + + it('should be thrown an error if the new address is not belong to the client', async() => { + const addressFk = 1; + try { + await models.Ticket.transferClient(ctx, originalTicketId, clientId, addressFk, options); + fail('Expected an error to be thrown, but none was thrown.'); + } catch (e) { + expect(e.message).toEqual('The address does not belong to the client'); + } + }); }); diff --git a/modules/ticket/back/methods/ticket/transferClient.js b/modules/ticket/back/methods/ticket/transferClient.js index 95bee008da..2e845144ed 100644 --- a/modules/ticket/back/methods/ticket/transferClient.js +++ b/modules/ticket/back/methods/ticket/transferClient.js @@ -1,3 +1,4 @@ +const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('transferClient', { description: 'Transferring ticket to another client', @@ -12,6 +13,10 @@ module.exports = Self => { arg: 'clientFk', type: 'number', required: true, + }, { + arg: 'addressFk', + type: 'number', + required: false, }], http: { path: `/:id/transferClient`, @@ -19,7 +24,7 @@ module.exports = Self => { } }); - Self.transferClient = async(ctx, id, clientFk, options) => { + Self.transferClient = async(ctx, id, clientFk, addressFk, options) => { const models = Self.app.models; const myOptions = {}; let tx; @@ -43,10 +48,12 @@ module.exports = Self => { const client = await models.Client.findById(clientFk, {fields: ['id', 'defaultAddressFk']}, myOptions); - const address = await models.Address.findById(client.defaultAddressFk, - {fields: ['id', 'nickname']}, myOptions); + const address = await models.Address.findById(addressFk ? addressFk : client.defaultAddressFk, + {fields: ['id', 'nickname', 'clientFk']}, myOptions); - const attributes = {clientFk, addressFk: client.defaultAddressFk, nickname: address.nickname}; + if (address.clientFk !== clientFk) throw new UserError('The address does not belong to the client'); + + const attributes = {clientFk, addressFk: address.id, nickname: address.nickname}; const tickets = []; const ticketIds = []; diff --git a/modules/travel/back/methods/travel/saveThermograph.js b/modules/travel/back/methods/travel/saveThermograph.js index 6f7e1c8bf6..2517b0b8c9 100644 --- a/modules/travel/back/methods/travel/saveThermograph.js +++ b/modules/travel/back/methods/travel/saveThermograph.js @@ -57,6 +57,10 @@ module.exports = Self => { arg: 'hasFileAttached', type: 'Boolean', description: 'True if has an attached file' + }, { + arg: 'agencyModeFk', + type: 'Number', + description: 'Carrier' }], returns: {type: 'object', root: true}, http: {path: `/:id/saveThermograph`, verb: 'POST'} @@ -76,6 +80,7 @@ module.exports = Self => { reference, description, hasFileAttached, + agencyModeFk, options ) => { const models = Self.app.models; @@ -119,6 +124,7 @@ module.exports = Self => { minTemperature, temperatureFk, warehouseFk: warehouseId, + agencyModeFk, }, myOptions); if (tx) await tx.commit(); diff --git a/modules/travel/back/methods/travel/specs/saveThermograph.spec.js b/modules/travel/back/methods/travel/specs/saveThermograph.spec.js index c2da4234ec..0f5f248bfb 100644 --- a/modules/travel/back/methods/travel/specs/saveThermograph.spec.js +++ b/modules/travel/back/methods/travel/specs/saveThermograph.spec.js @@ -9,6 +9,7 @@ describe('Thermograph saveThermograph()', () => { const maxTemperature = 30; const minTemperature = 10; const temperatureFk = 'COOL'; + const agencyModeFk = 1; let tx; let options; @@ -46,13 +47,16 @@ describe('Thermograph saveThermograph()', () => { null, null, null, - null, options + null, + agencyModeFk, + options ); expect(updatedThermograph.result).toEqual(state); expect(updatedThermograph.maxTemperature).toEqual(maxTemperature); expect(updatedThermograph.minTemperature).toEqual(minTemperature); expect(updatedThermograph.temperatureFk).toEqual(temperatureFk); + expect(updatedThermograph.agencyModeFk).toEqual(agencyModeFk); expect(updatedThermograph.dmsFk).toEqual(dmsFk); }); diff --git a/modules/travel/back/model-config.json b/modules/travel/back/model-config.json index 952ce0d20f..e7a6441800 100644 --- a/modules/travel/back/model-config.json +++ b/modules/travel/back/model-config.json @@ -22,5 +22,8 @@ }, "Temperature": { "dataSource": "vn" + }, + "AgencyModeIncoming": { + "dataSource": "vn" } -} \ No newline at end of file +} diff --git a/modules/travel/back/models/agency-mode-incoming.json b/modules/travel/back/models/agency-mode-incoming.json new file mode 100644 index 0000000000..efefaf8729 --- /dev/null +++ b/modules/travel/back/models/agency-mode-incoming.json @@ -0,0 +1,27 @@ +{ + "name": "AgencyModeIncoming", + "base": "VnModel", + "options": { + "mysql": { + "table": "agencyModeIncoming" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "name": { + "type": "string" + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] +} diff --git a/modules/travel/back/models/travel-thermograph.json b/modules/travel/back/models/travel-thermograph.json index cb0a9b4f8f..79b6925078 100644 --- a/modules/travel/back/models/travel-thermograph.json +++ b/modules/travel/back/models/travel-thermograph.json @@ -58,6 +58,11 @@ "type": "belongsTo", "model": "Thermograph", "foreignKey": "thermographFk" + }, + "agencyMode": { + "type": "belongsTo", + "model": "AgencyMode", + "foreignKey": "agencyModeFk" } } }