diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 4f85db98a..d918007e7 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -115,6 +115,9 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType` VALUES (1, 978, 1, 0, 2000, 9, 0); +INSERT INTO hedera.orderConfig (`id`, `employeeFk`, `defaultAgencyFk`, `guestMethod`, `guestAgencyFk`, `reserveTime`, `defaultCompanyFk`) + VALUES (1, 1, 2, 'PICKUP', 1, '00:20:00', 442); + INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`, `image`) VALUES (1101, 'brucewayne', 'Bruce Wayne', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 2, 1, 'BruceWayne@mydomain.com', 'es','1101'), @@ -301,6 +304,17 @@ UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 1 WHERE `id` = 8; UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 4 WHERE `id` = 23; UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 1 WHERE `id` = 10; +UPDATE `vn`.`agencyMode` SET `description` = 'inhouse pickup' WHERE `id` = 1; +UPDATE `vn`.`agencyMode` SET `description` = 'Super-Man delivery' WHERE `id` = 2; +UPDATE `vn`.`agencyMode` SET `description` = 'Teleportation device' WHERE `id` = 3; +UPDATE `vn`.`agencyMode` SET `description` = 'Entanglement' WHERE `id` = 4; +UPDATE `vn`.`agencyMode` SET `description` = 'Quantum break device' WHERE `id` = 5; +UPDATE `vn`.`agencyMode` SET `description` = 'Walking' WHERE `id` = 6; +UPDATE `vn`.`agencyMode` SET `description` = 'Gotham247' WHERE `id` = 7; +UPDATE `vn`.`agencyMode` SET `description` = 'Gotham247Expensive' WHERE `id` = 8; +UPDATE `vn`.`agencyMode` SET `description` = 'Other agency' WHERE `id` = 10; +UPDATE `vn`.`agencyMode` SET `description` = 'Refund' WHERE `id` = 23; + UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com'; UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23; @@ -979,6 +993,14 @@ INSERT INTO `vn`.`priceFixed`(`id`, `itemFk`, `rate0`, `rate1`, `rate2`, `rate3` (2, 3, 10, 10, 10, 10, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), 0, 1, util.VN_CURDATE()), (3, 13, 8.5, 10, 7.5, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), 1, 2, util.VN_CURDATE()); +INSERT INTO `vn`.`itemMinimumQuantity`(`itemFk`, `quantity`, `started`, `ended`, `warehouseFk`) + VALUES + (1, 5, util.VN_CURDATE() - INTERVAL 2 MONTH, util.VN_CURDATE() + INTERVAL 1 MONTH, 1), + (2, 10, util.VN_CURDATE() - INTERVAL 2 DAY, NULL, 2), + (3, 15, util.VN_CURDATE() + INTERVAL 3 DAY, util.VN_CURDATE() + INTERVAL 2 WEEK, 3), + (2, 10, util.VN_CURDATE() + INTERVAL 2 MONTH, NULL, 5), + (4, 8, util.VN_CURDATE() - INTERVAL 3 MONTH, NULL, NULL); + INSERT INTO `vn`.`expeditionBoxVol`(`boxFk`, `m3`, `ratio`) VALUES (71,0.141,1); diff --git a/db/routines/hedera/procedures/item_calcCatalog.sql b/db/routines/hedera/procedures/item_calcCatalog.sql index 4da878d23..fae89bd5c 100644 --- a/db/routines/hedera/procedures/item_calcCatalog.sql +++ b/db/routines/hedera/procedures/item_calcCatalog.sql @@ -25,12 +25,7 @@ BEGIN JOIN vn.warehouse w ON w.id = p.warehouseFk ORDER BY warehouseFk, `grouping`; - DROP TEMPORARY TABLE - tmp.ticketCalculateItem, - tmp.ticketComponentPrice, - tmp.ticketComponent, - tmp.ticketLot, - tmp.zoneGetShipped, - tmp.item; + CALL vn.ticketCalculatePurge(); + DROP TEMPORARY TABLE tmp.item; END$$ DELIMITER ; diff --git a/db/routines/hedera/procedures/order_addItem.sql b/db/routines/hedera/procedures/order_addItem.sql index d9c15c0eb..50c2f3489 100644 --- a/db/routines/hedera/procedures/order_addItem.sql +++ b/db/routines/hedera/procedures/order_addItem.sql @@ -56,11 +56,15 @@ BEGIN CALL util.throw ('ORDER_ROW_UNAVAILABLE'); END IF; - SELECT IFNULL(minQuantity, 0) INTO vMinQuantity - FROM vn.item - WHERE id = vItem; + SELECT quantity INTO vMinQuantity + FROM vn.itemMinimumQuantity + WHERE itemFk = vItem + AND `started` <= vShipment + AND (`ended` >= vShipment OR `ended` IS NULL) + AND (warehouseFk = vWarehouse OR warehouseFk IS NULL) + LIMIT 1; - IF vAmount < LEAST(vMinQuantity, vAvailable) THEN + IF vAmount < LEAST(IFNULL(vMinQuantity, 0), vAvailable) THEN CALL util.throw ('quantityLessThanMin'); END IF; diff --git a/db/routines/vn/procedures/catalog_calculate.sql b/db/routines/vn/procedures/catalog_calculate.sql index bb52020df..e79f2da0b 100644 --- a/db/routines/vn/procedures/catalog_calculate.sql +++ b/db/routines/vn/procedures/catalog_calculate.sql @@ -45,8 +45,7 @@ BEGIN CALL catalog_componentPrepare(); - DROP TEMPORARY TABLE IF EXISTS tmp.ticketCalculateItem; - CREATE TEMPORARY TABLE tmp.ticketCalculateItem( + CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem( itemFk INT(11) NOT NULL, available INT(11), producer VARCHAR(50), @@ -60,11 +59,11 @@ BEGIN price DECIMAL(10,2), priceKg DECIMAL(10,2), `grouping` INT(10) UNSIGNED, + minQuantity INT(10) UNSIGNED, PRIMARY KEY `itemFk` (`itemFk`) ) ENGINE = MEMORY DEFAULT CHARSET=utf8; OPEN cTravelTree; - l: LOOP SET vDone = FALSE; FETCH cTravelTree INTO vZoneFk, vWarehouseFk, vShipped; @@ -136,7 +135,7 @@ BEGIN CALL vn.catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk); - INSERT INTO tmp.ticketCalculateItem ( + INSERT INTO tmp.ticketCalculateItem( itemFk, available, producer, @@ -149,9 +148,9 @@ BEGIN origin, price, priceKg, - `grouping`) - SELECT - tl.itemFk, + `grouping`, + minQuantity) + SELECT tl.itemFk, SUM(tl.available) available, p.name producer, i.name item, @@ -163,7 +162,8 @@ BEGIN o.code origin, bl.price, bl.priceKg, - bl.`grouping` + bl.`grouping`, + mq.quantity FROM tmp.ticketLot tl JOIN item i ON tl.itemFk = i.id LEFT JOIN producer p ON p.id = i.producerFk AND p.isVisible @@ -179,12 +179,19 @@ BEGIN ) sub GROUP BY itemFk ) bl ON bl.itemFk = tl.itemFk + LEFT JOIN ( + SELECT itemFk, quantity, warehouseFk + FROM itemMinimumQuantity + WHERE `started` <= vShipped + AND (`ended` >= vShipped OR `ended` IS NULL) + GROUP BY itemFk, warehouseFk + ORDER BY warehouseFk DESC + ) mq ON mq.itemFk = tl.itemFk + AND (mq.warehouseFk = tl.warehouseFk OR mq.warehouseFk IS NULL) WHERE tl.zoneFk = vZoneFk AND tl.warehouseFk = vWarehouseFk GROUP BY tl.itemFk ON DUPLICATE KEY UPDATE available = available + VALUES(available); - END LOOP; - CLOSE cTravelTree; END$$ DELIMITER ; diff --git a/db/routines/vn/procedures/item_getSimilar.sql b/db/routines/vn/procedures/item_getSimilar.sql index 557d77bdf..72c543b40 100644 --- a/db/routines/vn/procedures/item_getSimilar.sql +++ b/db/routines/vn/procedures/item_getSimilar.sql @@ -63,7 +63,7 @@ BEGIN WHEN b.groupingMode = 'grouping' THEN b.grouping WHEN b.groupingMode = 'packing' THEN b.packing ELSE 1 - END AS minQuantity, + END minQuantity, v.visible located, b.price2 FROM vn.item i diff --git a/db/routines/vn/triggers/itemMinimumQuantity_afterDelete.sql b/db/routines/vn/triggers/itemMinimumQuantity_afterDelete.sql new file mode 100644 index 000000000..1da5a5942 --- /dev/null +++ b/db/routines/vn/triggers/itemMinimumQuantity_afterDelete.sql @@ -0,0 +1,12 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`itemMinimumQuantity_afterDelete` + AFTER DELETE ON `itemMinimumQuantity` + FOR EACH ROW +BEGIN + INSERT INTO itemLog + SET `action` = 'delete', + `changedModel` = 'ItemMinimumQuantity', + `changedModelId` = OLD.id, + `userFk` = account.myUser_getId(); +END$$ +DELIMITER ; diff --git a/db/routines/vn/triggers/itemMinimumQuantity_beforeInsert.sql b/db/routines/vn/triggers/itemMinimumQuantity_beforeInsert.sql new file mode 100644 index 000000000..8833ac968 --- /dev/null +++ b/db/routines/vn/triggers/itemMinimumQuantity_beforeInsert.sql @@ -0,0 +1,8 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`itemMinimumQuantity_beforeInsert` + BEFORE INSERT ON `itemMinimumQuantity` + FOR EACH ROW +BEGIN + SET NEW.editorFk = account.myUser_getId(); +END$$ +DELIMITER ; diff --git a/db/routines/vn/triggers/itemMinimumQuantity_beforeUpdate.sql b/db/routines/vn/triggers/itemMinimumQuantity_beforeUpdate.sql new file mode 100644 index 000000000..ef030f9f9 --- /dev/null +++ b/db/routines/vn/triggers/itemMinimumQuantity_beforeUpdate.sql @@ -0,0 +1,8 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`itemMinimumQuantity_beforeUpdate` + BEFORE UPDATE ON `itemMinimumQuantity` + FOR EACH ROW +BEGIN + SET NEW.editorFk = account.myUser_getId(); +END$$ +DELIMITER ; diff --git a/db/routines/vn/triggers/itemShelving _afterDelete.sql b/db/routines/vn/triggers/itemShelving _afterDelete.sql index 7c0d54358..9a1759eff 100644 --- a/db/routines/vn/triggers/itemShelving _afterDelete.sql +++ b/db/routines/vn/triggers/itemShelving _afterDelete.sql @@ -1,5 +1,5 @@ DELIMITER $$ -CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`itemShelving _afterDelete` +CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`itemShelving_afterDelete` AFTER DELETE ON `itemShelving` FOR EACH ROW BEGIN diff --git a/db/routines/vn2008/views/buySource.sql b/db/routines/vn2008/views/buySource.sql index 33226a1dc..850483833 100644 --- a/db/routines/vn2008/views/buySource.sql +++ b/db/routines/vn2008/views/buySource.sql @@ -54,8 +54,7 @@ AS SELECT `b`.`entryFk` AS `Id_Entrada`, `i`.`packingOut` AS `packingOut`, `b`.`itemOriginalFk` AS `itemOriginalFk`, `io`.`longName` AS `itemOriginalName`, - `it`.`gramsMax` AS `gramsMax`, - `i`.`minQuantity` AS `minQuantity` + `it`.`gramsMax` AS `gramsMax` FROM ( ( ( diff --git a/db/versions/11149-silverSalal/00-firstScript.sql b/db/versions/11149-silverSalal/00-firstScript.sql new file mode 100644 index 000000000..e642dd2e4 --- /dev/null +++ b/db/versions/11149-silverSalal/00-firstScript.sql @@ -0,0 +1,30 @@ +ALTER TABLE vn.item CHANGE minQuantity minQuantity__ int(10) unsigned DEFAULT + NULL NULL COMMENT '@deprecated 2024-07-11 refs #7704 Cantidad mínima para una línea de venta'; + +CREATE TABLE `vn`.`itemMinimumQuantity` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `itemFk` int(10) NOT NULL, + `quantity` int(10) NOT NULL, + `started` date NOT NULL, + `ended` date DEFAULT NULL, + `warehouseFk` smallint(5) unsigned DEFAULT NULL, + `created` timestamp NOT NULL DEFAULT current_timestamp(), + `editorFk` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `itemMinimumQuantity_UNIQUE` (`itemFk`, `started`, `ended`, `warehouseFk`), + KEY `itemFk` (`itemFk`), + KEY `started` (`started`), + KEY `ended` (`ended`), + KEY `warehouseFk` (`warehouseFk`), + KEY `editorFk` (`editorFk`), + CONSTRAINT `itemMinimumQuantity_ibfk_1` FOREIGN KEY (`itemFk`) REFERENCES `item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `itemMinimumQuantity_ibfk_2` FOREIGN KEY (`warehouseFk`) REFERENCES `warehouse` (`id`) ON UPDATE CASCADE, + CONSTRAINT `itemMinimumQuantity_ibfk_3` FOREIGN KEY (`editorFk`) REFERENCES `account`.`user` (`id`), + CONSTRAINT `itemMinimumQuantity_check_1` CHECK (`started` <= `ended`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +GRANT SELECT, UPDATE, DELETE, INSERT ON TABLE vn.itemMinimumQuantity TO buyer; +GRANT EXECUTE ON PROCEDURE vn.ticketCalculatePurge TO guest; + +ALTER TABLE vn.itemLog MODIFY COLUMN changedModel enum('Item','ItemBarcode','ItemBotanical','ItemNiche','ItemTag','ItemTaxCountry','ItemMinimumQuantity') + CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'Item' NOT NULL; diff --git a/modules/item/back/model-config.json b/modules/item/back/model-config.json index 2d06e1ada..5dbe4d62a 100644 --- a/modules/item/back/model-config.json +++ b/modules/item/back/model-config.json @@ -32,6 +32,9 @@ "ItemLog": { "dataSource": "vn" }, + "ItemMinimumQuantity": { + "dataSource": "vn" + }, "ItemPackingType": { "dataSource": "vn" }, diff --git a/modules/item/back/models/item-minimum-quantity.json b/modules/item/back/models/item-minimum-quantity.json new file mode 100644 index 000000000..74eac39fc --- /dev/null +++ b/modules/item/back/models/item-minimum-quantity.json @@ -0,0 +1,63 @@ +{ + "name": "ItemMinimumQuantity", + "base": "VnModel", + "options": { + "mysql": { + "table": "itemMinimumQuantity" + } + }, + "mixins": { + "Loggable": true + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Id" + }, + "itemFk": { + "type": "number", + "required": true + }, + "quantity": { + "type": "number", + "required": true + }, + "started": { + "type": "date", + "required": true + }, + "ended": { + "type": "date" + }, + "warehouseFk": { + "type": "number" + }, + "created": { + "type": "date" + }, + "editorFk": { + "type": "number" + } + }, + "relations": { + "item": { + "type": "belongsTo", + "model": "Item", + "foreignKey": "itemFk" + }, + "warehouse": { + "type": "belongsTo", + "model": "Warehouse", + "foreignKey": "warehouseFk" + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "buyer", + "permission": "ALLOW" + } + ] +} \ No newline at end of file diff --git a/modules/item/back/models/item.json b/modules/item/back/models/item.json index 10cff3e04..eda56e938 100644 --- a/modules/item/back/models/item.json +++ b/modules/item/back/models/item.json @@ -152,10 +152,6 @@ "columnName": "doPhoto" } }, - "minQuantity": { - "type": "number", - "description": "Min quantity" - }, "photoMotivation": { "type": "string" } diff --git a/modules/item/back/models/specs/itemMinimumQuantity.spec.js b/modules/item/back/models/specs/itemMinimumQuantity.spec.js new file mode 100644 index 000000000..072a80f94 --- /dev/null +++ b/modules/item/back/models/specs/itemMinimumQuantity.spec.js @@ -0,0 +1,101 @@ +const {models} = require('vn-loopback/server/server'); + +describe('itemMinimumQuantity model', () => { + const itemFk = 5; + const warehouseFk = 60; + + beforeAll(async() => { + await models.ItemMinimumQuantity.destroyAll({where: {itemFk: itemFk}}); + }); + + describe('CRUD operations', () => { + it('should create a new itemMinimumQuantity record', async() => { + const newRecord = { + itemFk: itemFk, + quantity: 100, + started: Date.vnNew(), + ended: new Date(Date.vnNew().setFullYear(Date.vnNew().getFullYear() + 1)), + warehouseFk: warehouseFk + }; + + const createdRecord = await models.ItemMinimumQuantity.create(newRecord); + + expect(createdRecord).toBeDefined(); + expect(createdRecord.quantity).toEqual(newRecord.quantity); + }); + + it('should read an existing itemMinimumQuantity record', async() => { + const newRecord = { + itemFk: itemFk, + quantity: 100, + started: Date.vnNew(), + ended: new Date(Date.vnNew().setFullYear(Date.vnNew().getFullYear() + 2)), + warehouseFk: warehouseFk + }; + + await models.ItemMinimumQuantity.create(newRecord); + + const existingRecord = await models.ItemMinimumQuantity.findOne({where: {itemFk: itemFk}}); + + expect(existingRecord).toBeDefined(); + expect(existingRecord.itemFk).toEqual(itemFk); + }); + + it('should update an existing itemMinimumQuantity record', async() => { + const newRecord = { + itemFk: itemFk, + quantity: 100, + started: Date.vnNew(), + ended: new Date(Date.vnNew().setFullYear(Date.vnNew().getFullYear() + 3)), + warehouseFk: warehouseFk + }; + + await models.ItemMinimumQuantity.create(newRecord); + + const newQuantity = 150; + const existingRecord = await models.ItemMinimumQuantity.findOne({where: {itemFk: itemFk}}); + existingRecord.quantity = newQuantity; + + await existingRecord.save(); + const updatedRecord = await models.ItemMinimumQuantity.findOne({where: {itemFk: itemFk}}); + + expect(updatedRecord.quantity).toEqual(newQuantity); + }); + }); + + describe('validation and constraints', () => { + it('should enforce unique constraint on itemFk, started, ended, and warehouseFk', async() => { + const newRecord = { + itemFk: itemFk, + quantity: 100, + started: Date.vnNew(), + ended: Date.vnNew().setFullYear(Date.vnNew().getFullYear() + 5), + warehouseFk: warehouseFk + }; + + try { + await models.ItemMinimumQuantity.create(newRecord); + await models.ItemMinimumQuantity.create(newRecord); + } catch (e) { + expect(e).toBeDefined(); + expect(e.code).toContain('ER_DUP_ENTRY'); + } + }); + + it('should allow null values for ended and warehouseFk', async() => { + const newRecord = { + itemFk: itemFk, + quantity: 100, + started: Date.vnNew(), + ended: null, + warehouseFk: null + }; + + const createdRecord = await models.ItemMinimumQuantity.create(newRecord); + + expect(createdRecord).toBeDefined(); + expect(createdRecord.ended).toBeNull(); + expect(createdRecord.warehouseFk).toBeNull(); + }); + }); +}); diff --git a/modules/item/front/summary/index.html b/modules/item/front/summary/index.html index 13c671d29..0e4cfc955 100644 --- a/modules/item/front/summary/index.html +++ b/modules/item/front/summary/index.html @@ -128,9 +128,6 @@ - -

diff --git a/modules/order/back/methods/order/catalogFilter.js b/modules/order/back/methods/order/catalogFilter.js index ab1d7784e..27048457c 100644 --- a/modules/order/back/methods/order/catalogFilter.js +++ b/modules/order/back/methods/order/catalogFilter.js @@ -119,7 +119,7 @@ module.exports = Self => { w.firstName, tci.priceKg, ink.hex, - i.minQuantity + tci.minQuantity FROM tmp.ticketCalculateItem tci JOIN vn.item i ON i.id = tci.itemFk JOIN vn.itemType it ON it.id = i.typeFk @@ -158,21 +158,19 @@ module.exports = Self => { // Apply item prices const pricesIndex = stmts.push( - `SELECT - tcp.itemFk, + `SELECT tcp.itemFk, tcp.grouping, tcp.price, tcp.rate, tcp.warehouseFk, tcp.priceKg, - w.name AS warehouse + w.name warehouse FROM tmp.ticketComponentPrice tcp JOIN vn.warehouse w ON w.id = tcp.warehouseFk`) - 1; // Get tags from all items const itemTagsIndex = stmts.push( - `SELECT - t.id, + `SELECT t.id, t.name, t.isFree, t.sourceTable, diff --git a/modules/order/front/catalog-view/index.html b/modules/order/front/catalog-view/index.html index 081ce05a0..562723cf7 100644 --- a/modules/order/front/catalog-view/index.html +++ b/modules/order/front/catalog-view/index.html @@ -37,18 +37,6 @@ value="{{::item.value7}}"> - - - - - - {{::item.minQuantity}} -