From 300b2c2a07135dad8e324608083399325e125143 Mon Sep 17 00:00:00 2001 From: Joan Date: Tue, 21 Aug 2018 13:38:16 +0200 Subject: [PATCH] catalog filter #581 --- client/order/routes.json | 8 +- client/order/src/catalog/index.html | 86 +++++++++++ .../order/src/{catalogue => catalog}/index.js | 11 +- client/order/src/catalogue/index.html | 34 ----- client/order/src/catalogue/product.html | 50 ------ client/order/src/catalogue/product.js | 24 --- client/order/src/filter/index.html | 46 +++++- client/order/src/filter/index.js | 73 ++++++++- client/order/src/filter/style.scss | 47 ++++++ client/order/src/index.js | 4 +- client/order/src/index/index.html | 4 +- client/order/src/prices-popover/index.html | 4 +- client/order/src/search-panel/index.html | 25 +++ client/order/src/search-panel/index.js | 7 + client/salix/src/styles/order-product.scss | 57 +++---- client/ticket/src/picture/index.html | 8 +- .../client/common/models/credit-insurance.js | 2 +- services/db/install/boot.sh | 8 +- .../db/install/changes/1.0.9/04-sample.sql | 4 +- .../install/changes/1.0.9/05-clientSample.sql | 4 +- .../{1.0.10 => 1.1.0}/01-claimBeginning.sql | 0 .../{1.0.10 => 1.1.0}/02-foreginKey.sql | 0 .../03-clientGetDebtDiary.sql | 0 .../{1.0.10 => 1.1.0}/04-orderListTax.sql | 0 .../05-ticketGetProblems.sql | 0 .../{1.0.10 => 1.1.0}/06-ticketFilter.sql | 0 .../07-agencyHourGetLanded.sql | 0 .../db/install/changes/1.1.0/08-reinos.sql | 13 ++ .../install/changes/1.1.0/09-itemCategory.sql | 14 ++ .../changes/1.1.0/10-ticketCalculate.sql | 117 +++++++++++++++ services/loopback/common/locale/en.json | 3 +- .../loopback/common/models/item-category.json | 45 ++++++ .../loopback/common/models/item-type.json | 7 +- services/loopback/common/models/vn-model.js | 2 +- .../loopback/server/connectors/vn-mysql.js | 142 +++++++++++++++++- services/loopback/server/model-config.json | 3 + .../common/methods/order/catalogFilter.js | 119 +++++++++++++++ .../order/common/methods/order/itemFilter.js | 61 -------- .../methods/order/specs/catalogFilter.spec.js | 17 +++ .../methods/order/specs/itemFilter.spec.js | 17 --- services/order/common/models/order.js | 2 +- 41 files changed, 811 insertions(+), 257 deletions(-) create mode 100644 client/order/src/catalog/index.html rename client/order/src/{catalogue => catalog}/index.js (81%) delete mode 100644 client/order/src/catalogue/index.html delete mode 100644 client/order/src/catalogue/product.html delete mode 100644 client/order/src/catalogue/product.js create mode 100644 client/order/src/filter/style.scss create mode 100644 client/order/src/search-panel/index.html create mode 100644 client/order/src/search-panel/index.js rename services/db/install/changes/{1.0.10 => 1.1.0}/01-claimBeginning.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/02-foreginKey.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/03-clientGetDebtDiary.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/04-orderListTax.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/05-ticketGetProblems.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/06-ticketFilter.sql (100%) rename services/db/install/changes/{1.0.10 => 1.1.0}/07-agencyHourGetLanded.sql (100%) create mode 100644 services/db/install/changes/1.1.0/08-reinos.sql create mode 100644 services/db/install/changes/1.1.0/09-itemCategory.sql create mode 100644 services/db/install/changes/1.1.0/10-ticketCalculate.sql create mode 100644 services/loopback/common/models/item-category.json create mode 100644 services/order/common/methods/order/catalogFilter.js delete mode 100644 services/order/common/methods/order/itemFilter.js create mode 100644 services/order/common/methods/order/specs/catalogFilter.spec.js delete mode 100644 services/order/common/methods/order/specs/itemFilter.spec.js diff --git a/client/order/routes.json b/client/order/routes.json index 1a6b10c66..26ed4da75 100644 --- a/client/order/routes.json +++ b/client/order/routes.json @@ -34,10 +34,10 @@ } }, { - "url": "/catalogue", - "state": "order.card.catalogue", - "component": "vn-order-catalogue", - "description": "Catalogue", + "url": "/catalog?q", + "state": "order.card.catalog", + "component": "vn-order-catalog", + "description": "Catalog", "params": { "order": "$ctrl.order" }, diff --git a/client/order/src/catalog/index.html b/client/order/src/catalog/index.html new file mode 100644 index 000000000..59dea430c --- /dev/null +++ b/client/order/src/catalog/index.html @@ -0,0 +1,86 @@ + + + + + + + + + {{model.data.length}} results + - + + +
+ + + + + + + +

+ {{::item.name}} +

+ + {{::item.subName}} + + + + + + + + + + + + + + {{::item.available}} + from + {{::item.price | currency: ' €': 2}} + + + + + + + +
+
+
+
+
+
+ + + No results + + +
+
+
+ + + + +
+ + + diff --git a/client/order/src/catalogue/index.js b/client/order/src/catalog/index.js similarity index 81% rename from client/order/src/catalogue/index.js rename to client/order/src/catalog/index.js index cee1972a5..a38112cc2 100644 --- a/client/order/src/catalogue/index.js +++ b/client/order/src/catalog/index.js @@ -17,10 +17,8 @@ class Controller { if (!value) return; this.filter = { - where: { - id: value.id, - typeFk: 1 - } + orderFk: value.id, + where: {} }; } @@ -28,7 +26,8 @@ class Controller { return this._order; } - openPricePopover(event, item) { + preview(event, item) { + event.preventDefault(); this.$scope.pricesPopover.show(event, item); } @@ -39,7 +38,7 @@ class Controller { Controller.$inject = ['$scope', '$stateParams']; -ngModule.component('vnOrderCatalogue', { +ngModule.component('vnOrderCatalog', { template: require('./index.html'), controller: Controller, bindings: { diff --git a/client/order/src/catalogue/index.html b/client/order/src/catalogue/index.html deleted file mode 100644 index 6e41c1f6e..000000000 --- a/client/order/src/catalogue/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - {{model.data.length}} results - - - - - - - No results - - - - - - - - - - - - diff --git a/client/order/src/catalogue/product.html b/client/order/src/catalogue/product.html deleted file mode 100644 index 6cd775f4f..000000000 --- a/client/order/src/catalogue/product.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - -

- {{::$ctrl.item.name}} -

- - {{::$ctrl.item.subName}} - - - - - - - - - - - - - - {{::$ctrl.item.available}} - from - {{::$ctrl.item.price | currency: ' €': 2}} - - - - - - - -
-
-
-
\ No newline at end of file diff --git a/client/order/src/catalogue/product.js b/client/order/src/catalogue/product.js deleted file mode 100644 index ca2e4e1cb..000000000 --- a/client/order/src/catalogue/product.js +++ /dev/null @@ -1,24 +0,0 @@ -import ngModule from '../module'; - -class Controller { - onClick(event) { - if (event.defaultPrevented) - event.stopImmediatePropagation(); - } - - preview(event) { - event.preventDefault(); - this.index.openPricePopover(event, this.item); - } -} - -ngModule.component('vnOrderProduct', { - template: require('./product.html'), - controller: Controller, - bindings: { - item: '<' - }, - require: { - index: '^vnOrderCatalogue' - } -}); diff --git a/client/order/src/filter/index.html b/client/order/src/filter/index.html index 5431b752a..369e81500 100644 --- a/client/order/src/filter/index.html +++ b/client/order/src/filter/index.html @@ -1,9 +1,49 @@ + + + - - - + + + + + + + + + + + + + + + + + + + + + + tags + diff --git a/client/order/src/filter/index.js b/client/order/src/filter/index.js index f3d831bac..1380db835 100644 --- a/client/order/src/filter/index.js +++ b/client/order/src/filter/index.js @@ -1,16 +1,81 @@ import ngModule from '../module'; +import './style.scss'; class Controller { - setFilter() { - this.catalogue.applyFilter(); + constructor($http) { + this.$http = $http; + this.itemTypes = []; + } + + get where() { + return this.catalog.filter.where; + } + + set categoryFk(value) { + if (this.where['it.categoryFk'] == value) { + this.where['it.categoryFk'] = null; + this.where['i.typeFk'] = null; + this.itemTypes = []; + + return; + } + + this.where['it.categoryFk'] = value; + this.where['i.typeFk'] = null; + + let query = `/item/api/ItemCategories/${value}/itemTypes`; + + this.$http.get(query).then(res => { + this.itemTypes = res.data; + }); + } + + get categoryFk() { + return this.where['it.categoryFk']; + } + + set typeFk(value) { + this.where['i.typeFk'] = value; + + this.catalog.applyFilter(); + } + + get typeFk() { + return this.where['i.typeFk']; + } + + onSearch(filter) { + if (!Object.keys(filter).length) return; + + if (filter.search) { + console.log(this.exprBuilder('search', filter.search)); + Object.assign(this.where, this.exprBuilder('search', filter.search)); + } else { + Object.assign(this.where, filter); + console.log(this.where); + } + + this.catalog.applyFilter(); + } + + exprBuilder(param, value) { + switch (param) { + case 'search': + return {'itg.value': {like: value}}; + case 'itg.tagFk': + case 'itg.value': + return {[param]: value}; + } } } -ngModule.component('vnFilter', { +Controller.$inject = ['$http']; + +ngModule.component('vnCatalogFilter', { template: require('./index.html'), controller: Controller, require: { - catalogue: '^vnOrderCatalogue' + catalog: '^vnOrderCatalog' }, bindings: { order: '<' diff --git a/client/order/src/filter/style.scss b/client/order/src/filter/style.scss new file mode 100644 index 000000000..e2d5add55 --- /dev/null +++ b/client/order/src/filter/style.scss @@ -0,0 +1,47 @@ +@import "colors"; + +vn-catalog-filter { +/* .item-category, .item-category * { + + + vn-button button { + border-radius: 50%; + padding: 1em; + width: 4.9em; + height: 4.9em; + + + vn-icon i { + font-size: 32pt + } + } + } */ + + .item-category { + justify-content: flex-start; + align-items: flex-start; + flex-wrap: wrap; + + vn-icon { + background-color: $secondary-font-color; + border-radius: 50%; + cursor: pointer; + + i:before { + font-size: 32pt; + width: 1em; + height: 1em; + } + } + + vn-icon.active { + background-color: $main-01; + color: #FFF + } + + & > vn-one { + width: 33.33%; + text-align: center + } + } +} \ No newline at end of file diff --git a/client/order/src/index.js b/client/order/src/index.js index 4e58591ce..cb618a97e 100644 --- a/client/order/src/index.js +++ b/client/order/src/index.js @@ -2,11 +2,11 @@ export * from './module'; import './card'; import './descriptor'; +import './search-panel'; import './filter'; import './index/'; import './summary'; -import './catalogue'; -import './catalogue/product'; +import './catalog'; import './line'; import './prices-popover'; import './volume'; diff --git a/client/order/src/index/index.html b/client/order/src/index/index.html index 48b5a03f9..a74f83da1 100644 --- a/client/order/src/index/index.html +++ b/client/order/src/index/index.html @@ -18,11 +18,11 @@ + ui-sref="order.card.catalog({id: {{::order.id}}})"> {{::order.id}} {{::order.clientFk}} {{::order.companyFk}} - {{::order.created}} + {{::order.created | date:'dd/MM/yyyy'}} diff --git a/client/order/src/prices-popover/index.html b/client/order/src/prices-popover/index.html index 709d251e3..132058e7e 100644 --- a/client/order/src/prices-popover/index.html +++ b/client/order/src/prices-popover/index.html @@ -20,7 +20,7 @@ value="{{$ctrl.item.name}}"> + value="{{$ctrl.item.firstName}} {{$ctrl.item.lastName}}"> - {{::price.warehouseName}} + {{::price.warehouse}} {{::price.grouping}} x {{::price.price | currency: ' €': 2}} diff --git a/client/order/src/search-panel/index.html b/client/order/src/search-panel/index.html new file mode 100644 index 000000000..dcb02fc1b --- /dev/null +++ b/client/order/src/search-panel/index.html @@ -0,0 +1,25 @@ +
+
+ + + {{name}} + + + + + + + + + +
+
diff --git a/client/order/src/search-panel/index.js b/client/order/src/search-panel/index.js new file mode 100644 index 000000000..90c8fa77d --- /dev/null +++ b/client/order/src/search-panel/index.js @@ -0,0 +1,7 @@ +import ngModule from '../module'; +import SearchPanel from 'core/src/components/searchbar/search-panel'; + +ngModule.component('vnOrderSearchPanel', { + template: require('./index.html'), + controller: SearchPanel +}); diff --git a/client/salix/src/styles/order-product.scss b/client/salix/src/styles/order-product.scss index c1560fa59..0a0243e08 100644 --- a/client/salix/src/styles/order-product.scss +++ b/client/salix/src/styles/order-product.scss @@ -2,45 +2,45 @@ - @media screen and (max-width: 1920px){ - vn-order-product { - width: 25%; - } +@media screen and (max-width: 1920px){ + .catalog-list .product { + width: 25%; } +} - @media screen and (max-width: 1800px){ - vn-order-product { - width: 33.33% - } +@media screen and (max-width: 1800px){ + .catalog-list .product { + width: 33.33% } +} - @media screen and (max-width: 1600px){ - vn-order-product { - width: 50% - } +@media screen and (max-width: 1600px){ + .catalog-list .product { + width: 50% } +} - @media screen and (max-width: 1280px){ - vn-order-product { - width: 100% - } +@media screen and (max-width: 1280px){ + .catalog-list .product { + width: 100% } +} - .catalogue.header { - border-color: $lines; - border-bottom: 1px solid rgba($lines, 0.5); +.catalog-header { + border-color: $lines; + border-bottom: 1px solid rgba($lines, 0.5); - span { - color: $secondary-font-color - } + span { + color: $secondary-font-color } +} - .catalogue.list { - justify-content: flex-start; - align-items: flex-start; - flex-wrap: wrap - } - vn-order-product { +.catalog-list { + justify-content: flex-start; + align-items: flex-start; + flex-wrap: wrap; + + .product { box-sizing: border-box; padding: 4px; @@ -108,3 +108,4 @@ } } } +} diff --git a/client/ticket/src/picture/index.html b/client/ticket/src/picture/index.html index 1308988f2..c6b2764ac 100644 --- a/client/ticket/src/picture/index.html +++ b/client/ticket/src/picture/index.html @@ -11,8 +11,8 @@ Pictures - - + +
@@ -54,12 +54,12 @@ by {{::sale.price | currency: ' €': 2}} -
+
- +
diff --git a/services/client/common/models/credit-insurance.js b/services/client/common/models/credit-insurance.js index 495f7db24..2c92ad1d1 100644 --- a/services/client/common/models/credit-insurance.js +++ b/services/client/common/models/credit-insurance.js @@ -28,7 +28,7 @@ module.exports = function(Self) { }; let insurance = await Self.findOne(filter); - if (insurance && (!insurance.grade && this.grade || insurance.grade && ! this.grade)) + if (insurance && (!insurance.grade && this.grade || insurance.grade && !this.grade)) err(); done(); diff --git a/services/db/install/boot.sh b/services/db/install/boot.sh index e727bf25d..0885d62a1 100644 --- a/services/db/install/boot.sh +++ b/services/db/install/boot.sh @@ -1,25 +1,25 @@ #!/bin/bash +export MYSQL_PWD=root if [ -d /data/mysql ]; then cp -R /data/mysql /var/lib echo "Restored database to default state" else - # Dump structure for file in dump/*-*.sql; do echo "Imported $file" - mysql -u root -proot -fc < $file + mysql -u root -fc < $file done # Import changes for file in changes/*/*.sql; do echo "Imported $file" - mysql -u root -proot -fc < $file + mysql -u root -fc < $file done # Import fixtures echo "Imported fixtures.sql" - mysql -u root -proot -f < dump/fixtures.sql + mysql -u root -f < dump/fixtures.sql # Copy dumpted data to volume cp -R /var/lib/mysql /data diff --git a/services/db/install/changes/1.0.9/04-sample.sql b/services/db/install/changes/1.0.9/04-sample.sql index d71216040..7a9c4faf4 100644 --- a/services/db/install/changes/1.0.9/04-sample.sql +++ b/services/db/install/changes/1.0.9/04-sample.sql @@ -16,6 +16,4 @@ VIEW `sample` AS `e`.`visible` AS `isVisible`, `e`.`hasCompany` AS `hasCompany` FROM - `vn2008`.`escritos` `e`; - -DROP VIEW `vn`.`clientNotificationType`; \ No newline at end of file + `vn2008`.`escritos` `e`; \ No newline at end of file diff --git a/services/db/install/changes/1.0.9/05-clientSample.sql b/services/db/install/changes/1.0.9/05-clientSample.sql index 26720137b..92b2f3d4a 100644 --- a/services/db/install/changes/1.0.9/05-clientSample.sql +++ b/services/db/install/changes/1.0.9/05-clientSample.sql @@ -13,6 +13,4 @@ VIEW `clientSample` AS `e`.`userFk` AS `userFk`, `e`.`empresa_id` AS `companyFk` FROM - `vn2008`.`escritos_det` `e`; - -DROP VIEW `vn`.`clientNotification`; \ No newline at end of file + `vn2008`.`escritos_det` `e`; \ No newline at end of file diff --git a/services/db/install/changes/1.0.10/01-claimBeginning.sql b/services/db/install/changes/1.1.0/01-claimBeginning.sql similarity index 100% rename from services/db/install/changes/1.0.10/01-claimBeginning.sql rename to services/db/install/changes/1.1.0/01-claimBeginning.sql diff --git a/services/db/install/changes/1.0.10/02-foreginKey.sql b/services/db/install/changes/1.1.0/02-foreginKey.sql similarity index 100% rename from services/db/install/changes/1.0.10/02-foreginKey.sql rename to services/db/install/changes/1.1.0/02-foreginKey.sql diff --git a/services/db/install/changes/1.0.10/03-clientGetDebtDiary.sql b/services/db/install/changes/1.1.0/03-clientGetDebtDiary.sql similarity index 100% rename from services/db/install/changes/1.0.10/03-clientGetDebtDiary.sql rename to services/db/install/changes/1.1.0/03-clientGetDebtDiary.sql diff --git a/services/db/install/changes/1.0.10/04-orderListTax.sql b/services/db/install/changes/1.1.0/04-orderListTax.sql similarity index 100% rename from services/db/install/changes/1.0.10/04-orderListTax.sql rename to services/db/install/changes/1.1.0/04-orderListTax.sql diff --git a/services/db/install/changes/1.0.10/05-ticketGetProblems.sql b/services/db/install/changes/1.1.0/05-ticketGetProblems.sql similarity index 100% rename from services/db/install/changes/1.0.10/05-ticketGetProblems.sql rename to services/db/install/changes/1.1.0/05-ticketGetProblems.sql diff --git a/services/db/install/changes/1.0.10/06-ticketFilter.sql b/services/db/install/changes/1.1.0/06-ticketFilter.sql similarity index 100% rename from services/db/install/changes/1.0.10/06-ticketFilter.sql rename to services/db/install/changes/1.1.0/06-ticketFilter.sql diff --git a/services/db/install/changes/1.0.10/07-agencyHourGetLanded.sql b/services/db/install/changes/1.1.0/07-agencyHourGetLanded.sql similarity index 100% rename from services/db/install/changes/1.0.10/07-agencyHourGetLanded.sql rename to services/db/install/changes/1.1.0/07-agencyHourGetLanded.sql diff --git a/services/db/install/changes/1.1.0/08-reinos.sql b/services/db/install/changes/1.1.0/08-reinos.sql new file mode 100644 index 000000000..8470be879 --- /dev/null +++ b/services/db/install/changes/1.1.0/08-reinos.sql @@ -0,0 +1,13 @@ +USE `vn2008`; + +ALTER TABLE `vn2008`.`reinos` +ADD COLUMN `icon` VARCHAR(45) NULL AFTER `mercancia`; + +INSERT INTO `vn2008`.`reinos` (`reino`, `display`, `icon`) VALUES +('Handmade', '1', 'icon-handmade'), +('Artificial', '1', 'icon-artificial'), +('Green', '1', 'icon-greenery'), +('Accessories', '1', 'icon-accessory'); + +UPDATE `vn2008`.`reinos` SET `icon`='icon-plant' WHERE `id`=1; +UPDATE `vn2008`.`reinos` SET `icon`='icon-flower' WHERE `id`=2; \ No newline at end of file diff --git a/services/db/install/changes/1.1.0/09-itemCategory.sql b/services/db/install/changes/1.1.0/09-itemCategory.sql new file mode 100644 index 000000000..cfb9781c0 --- /dev/null +++ b/services/db/install/changes/1.1.0/09-itemCategory.sql @@ -0,0 +1,14 @@ +USE `vn`; +CREATE + OR REPLACE ALGORITHM = UNDEFINED + DEFINER = `root`@`%` + SQL SECURITY DEFINER +VIEW `itemCategory` AS + SELECT + `r`.`id` AS `id`, + `r`.`reino` AS `name`, + `r`.`display` AS `display`, + `r`.`color` AS `color`, + r.icon + FROM + `vn2008`.`reinos` `r`; diff --git a/services/db/install/changes/1.1.0/10-ticketCalculate.sql b/services/db/install/changes/1.1.0/10-ticketCalculate.sql new file mode 100644 index 000000000..ae5f91804 --- /dev/null +++ b/services/db/install/changes/1.1.0/10-ticketCalculate.sql @@ -0,0 +1,117 @@ +USE `vn`; +DROP procedure IF EXISTS `ticketCalculate`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `ticketCalculate`( + vDate DATE, + vAddress INT, + vAgencyMode INT) +proc: BEGIN +/** + * Calcula los articulos disponibles y sus precios + * + * @param vDate Fecha de recepcion de mercancia + * @param vAddress Id del consignatario + * @param vAgencyMode Id de la agencia + * @return tmp.ticketCalculateItem, tmp.ticketComponentPrice + **/ + + DECLARE vAvailableCalc INT; + DECLARE vShipment DATE; + DECLARE vAgencyId INT; + DECLARE vClient INT; + DECLARE vWarehouseFk SMALLINT; + DECLARE vDone BOOL; + DECLARE cTravelTree CURSOR FOR + SELECT warehouseFk, shipped FROM tmp.agencyHourGetShipped; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + + -- Establece los almacenes y las fechas que van a entrar al disponible + + SELECT agencyFk INTO vAgencyId + FROM agencyMode WHERE id = vAgencyMode; + + SELECT clientFk INTO vClient + FROM address WHERE id = vAddress; + + CALL vn.agencyHourGetShipped(vDate, vAddress, vAgencyId); + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot; + CREATE TEMPORARY TABLE tmp.ticketLot( + `warehouseFk` smallint(5) unsigned NOT NULL, + `itemFk` int(11) NOT NULL, + `available` double DEFAULT NULL, + `buyFk` int(11) DEFAULT NULL, + `fix` tinyint(3) unsigned DEFAULT '0', + KEY `itemFk` (`itemFk`), + KEY `item_warehouse` (`itemFk`,`warehouseFk`) USING HASH + ) ENGINE=MEMORY DEFAULT CHARSET=utf8; + + OPEN cTravelTree; + + l: LOOP + SET vDone = FALSE; + FETCH cTravelTree INTO vWarehouseFk, vShipment; + + IF vDone THEN + LEAVE l; + END IF; + + CALL `cache`.available_refresh (vAvailableCalc, FALSE, vWarehouseFk, vShipment); + CALL buyUltimate (vWarehouseFk, vShipment); + + INSERT INTO tmp.ticketLot (warehouseFk, itemFk, available, buyFk) + SELECT + vWarehouseFk, + i.item_id, + IFNULL(i.available, 0), + bu.buyFk + FROM `cache`.available i + JOIN tmp.item br ON br.itemFk = i.item_id + LEFT JOIN item it ON it.id = i.item_id + LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = i.item_id + WHERE i.calc_id = vAvailableCalc + AND it.id != 100 + AND i.available > 0; + + DROP TEMPORARY TABLE tmp.buyUltimate; + END LOOP; + + CLOSE cTravelTree; + + CALL vn.ticketComponentCalculate(vAddress, vAgencyMode); + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketCalculateItem; + CREATE TEMPORARY TABLE tmp.ticketCalculateItem + ENGINE = MEMORY + SELECT + b.itemFk, + SUM(b.available) available, + p.name producer, + i.name item, + i.size size, + i.stems, + i.category, + i.inkFk, + i.image, + o.code origin, bl.price + FROM tmp.ticketLot b + JOIN item i ON b.itemFk = i.id + LEFT JOIN producer p ON p.id = i.producerFk AND p.isVisible + JOIN origin o ON o.id = i.originFk + JOIN ( + SELECT MIN(price) price, itemFk + FROM tmp.ticketComponentPrice + GROUP BY itemFk + ) bl ON bl.itemFk = b.itemFk + GROUP BY b.itemFk; + + DROP TEMPORARY TABLE + tmp.ticketComponent, + tmp.ticketLot; +END$$ + +DELIMITER ; + diff --git a/services/loopback/common/locale/en.json b/services/loopback/common/locale/en.json index b055144c5..8a250665d 100644 --- a/services/loopback/common/locale/en.json +++ b/services/loopback/common/locale/en.json @@ -29,5 +29,6 @@ "The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero", "The grade must be an integer greater than or equal to zero": "The grade must be an integer greater than or equal to zero", "Sample type cannot be blank": "Sample type cannot be blank", - "The new quantity should be smaller than the old one": "La nueva cantidad debe ser menor que la anterior" + "The new quantity should be smaller than the old one": "La nueva cantidad debe ser menor que la anterior", + "The package cannot be blank": "The package cannot be blank" } \ No newline at end of file diff --git a/services/loopback/common/models/item-category.json b/services/loopback/common/models/item-category.json new file mode 100644 index 000000000..fe6d804d8 --- /dev/null +++ b/services/loopback/common/models/item-category.json @@ -0,0 +1,45 @@ +{ + "name": "ItemCategory", + "base": "VnModel", + "options": { + "mysql": { + "table": "itemCategory" + } + }, + "properties": { + "id": { + "type": "Number", + "id": true, + "description": "Identifier" + }, + "name": { + "type": "String" + }, + "display": { + "type": "Boolean" + }, + "icon": { + "type": "String" + } + }, + "relations": { + "itemTypes": { + "type": "hasMany", + "model": "ItemType", + "foreignKey": "categoryFk" + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ], + "scope": { + "where": { + "display": {"gte": 1} + } + } +} \ No newline at end of file diff --git a/services/loopback/common/models/item-type.json b/services/loopback/common/models/item-type.json index 5dbdf6c91..b9f038f84 100644 --- a/services/loopback/common/models/item-type.json +++ b/services/loopback/common/models/item-type.json @@ -35,7 +35,12 @@ "type": "belongsTo", "model": "Warehouse", "foreignKey": "warehouseFk" - } + }, + "category": { + "type": "belongsTo", + "model": "ItemCategory", + "foreignKey": "categoryFk" + } }, "acls": [ { diff --git a/services/loopback/common/models/vn-model.js b/services/loopback/common/models/vn-model.js index f1e19351d..85b5e1650 100644 --- a/services/loopback/common/models/vn-model.js +++ b/services/loopback/common/models/vn-model.js @@ -197,7 +197,7 @@ module.exports = function(Self) { return sql + connector.columnEscaped(model, property); }; - return wrappedConnector.buildWhere(this.modelName, filter.where); + return wrappedConnector.makeWhere(this.modelName, filter.where); }; Self.buildLimit = function(filter) { diff --git a/services/loopback/server/connectors/vn-mysql.js b/services/loopback/server/connectors/vn-mysql.js index 27f200975..0f63211c6 100644 --- a/services/loopback/server/connectors/vn-mysql.js +++ b/services/loopback/server/connectors/vn-mysql.js @@ -1,8 +1,11 @@ var mysql = require('mysql'); -var SqlConnector = require('loopback-connector').SqlConnector; +const loopbackConnector = require('loopback-connector'); +const SqlConnector = loopbackConnector.SqlConnector; +const ParameterizedSQL = loopbackConnector.ParameterizedSQL; var MySQL = require('loopback-connector-mysql').MySQL; var EnumFactory = require('loopback-connector-mysql').EnumFactory; +var debug = require('debug')('loopback-connector-sql'); exports.initialize = function(dataSource, callback) { dataSource.driver = mysql; @@ -53,3 +56,140 @@ VnMySQL.prototype.toColumnValue = function(prop, val) { return v < 10 ? '0' + v : v; } }; + +/** + * Private make method + * @param {Object} model Model instance + * @param {Object} where Where filter + * @return {ParameterizedSQL} Parametized object + */ +VnMySQL.prototype._makeWhere = function(model, where) { + let columnValue; + let sqlExp; + + if (!where) { + return new ParameterizedSQL(''); + } + if (typeof where !== 'object' || Array.isArray(where)) { + debug('Invalid value for where: %j', where); + return new ParameterizedSQL(''); + } + var self = this; + var props = self.getModelDefinition(model).properties; + + var whereStmts = []; + for (var key in where) { + var stmt = new ParameterizedSQL('', []); + // Handle and/or operators + if (key === 'and' || key === 'or') { + var branches = []; + var branchParams = []; + var clauses = where[key]; + if (Array.isArray(clauses)) { + for (var i = 0, n = clauses.length; i < n; i++) { + var stmtForClause = self._makeWhere(model, clauses[i]); + if (stmtForClause.sql) { + stmtForClause.sql = '(' + stmtForClause.sql + ')'; + branchParams = branchParams.concat(stmtForClause.params); + branches.push(stmtForClause.sql); + } + } + stmt.merge({ + sql: branches.join(' ' + key.toUpperCase() + ' '), + params: branchParams, + }); + whereStmts.push(stmt); + continue; + } + // The value is not an array, fall back to regular fields + } + var p = props[key]; + + // eslint-disable one-var + var expression = where[key]; + var columnName = self.columnEscaped(model, key); + // eslint-enable one-var + if (expression === null || expression === undefined) { + stmt.merge(columnName + ' IS NULL'); + } else if (expression && expression.constructor === Object) { + var operator = Object.keys(expression)[0]; + // Get the expression without the operator + expression = expression[operator]; + if (operator === 'inq' || operator === 'nin' || operator === 'between') { + columnValue = []; + if (Array.isArray(expression)) { + // Column value is a list + for (var j = 0, m = expression.length; j < m; j++) { + columnValue.push(this.toColumnValue(p, expression[j])); + } + } else { + columnValue.push(this.toColumnValue(p, expression)); + } + if (operator === 'between') { + // BETWEEN v1 AND v2 + var v1 = columnValue[0] === undefined ? null : columnValue[0]; + var v2 = columnValue[1] === undefined ? null : columnValue[1]; + columnValue = [v1, v2]; + } else { + // IN (v1,v2,v3) or NOT IN (v1,v2,v3) + if (columnValue.length === 0) { + if (operator === 'inq') { + columnValue = [null]; + } else { + // nin () is true + continue; + } + } + } + } else if (operator === 'regexp' && expression instanceof RegExp) { + // do not coerce RegExp based on property definitions + columnValue = expression; + } else { + columnValue = this.toColumnValue(p, expression); + } + sqlExp = self.buildExpression(columnName, operator, columnValue, p); + stmt.merge(sqlExp); + } else { + // The expression is the field value, not a condition + columnValue = self.toColumnValue(p, expression); + if (columnValue === null) { + stmt.merge(columnName + ' IS NULL'); + } else { + if (columnValue instanceof ParameterizedSQL) { + stmt.merge(columnName + '=').merge(columnValue); + } else { + stmt.merge({ + sql: columnName + '=?', + params: [columnValue], + }); + } + } + } + whereStmts.push(stmt); + } + var params = []; + var sqls = []; + for (var k = 0, s = whereStmts.length; k < s; k++) { + sqls.push(whereStmts[k].sql); + params = params.concat(whereStmts[k].params); + } + var whereStmt = new ParameterizedSQL({ + sql: sqls.join(' AND '), + params: params, + }); + return whereStmt; +}; + +/** + * Build the SQL WHERE clause for the where object + * @param {string} model Model name + * @param {object} where An object for the where conditions + * @return {ParameterizedSQL} The SQL WHERE clause + */ +VnMySQL.prototype.makeWhere = function(model, where) { + var whereClause = this._makeWhere(model, where); + if (whereClause.sql) { + whereClause.sql = 'WHERE ' + whereClause.sql; + } + return whereClause; +}; diff --git a/services/loopback/server/model-config.json b/services/loopback/server/model-config.json index 38d63947e..d80927479 100644 --- a/services/loopback/server/model-config.json +++ b/services/loopback/server/model-config.json @@ -90,6 +90,9 @@ "ItemType": { "dataSource": "vn" }, + "ItemCategory": { + "dataSource": "vn" + }, "Expence": { "dataSource": "vn" }, diff --git a/services/order/common/methods/order/catalogFilter.js b/services/order/common/methods/order/catalogFilter.js new file mode 100644 index 000000000..1666f8ef2 --- /dev/null +++ b/services/order/common/methods/order/catalogFilter.js @@ -0,0 +1,119 @@ +const ParameterizedSQL = require('vn-loopback/node_modules/loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('catalogFilter', { + description: 'Find all instances of the model matched by filter from the data source.', + accessType: 'READ', + accepts: [ + { + arg: 'filter', + type: 'Object', + description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', + http: {source: 'query'} + } + ], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/catalogFilter`, + verb: 'GET' + } + }); + + Self.catalogFilter = async filter => { + let stmts = []; + let stmt; + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.item'); + + stmt = new ParameterizedSQL( + `CREATE TEMPORARY TABLE tmp.item + (PRIMARY KEY (itemFk)) ENGINE = MEMORY + SELECT + i.id AS itemFk, + i.typeFk, + it.categoryFk + FROM vn.item i + JOIN vn.itemType it ON it.id = i.typeFk + JOIN vn.itemCategory ic ON ic.id = it.categoryFk` + ); + + if (filter.where['itg.tagFk'] || filter.where['itg.value']) { + stmt.merge('JOIN vn.itemTag itg ON itg.itemFk = i.id'); + } + + stmt.merge(Self.buildSuffix(filter)); + stmts.push(stmt); + + let order = await Self.findById(filter.orderFk); + stmts.push(new ParameterizedSQL( + 'CALL vn.ticketCalculate(?, ?, ?)', [ + order.landed, + order.address_id, + order.agency_id + ] + )); + + let itemsIndex = stmts.push( + `SELECT + i.id, + i.name, + i.subName, + i.image, + i.tag5, + i.value5, + i.tag6, + i.value6, + i.tag7, + i.value7, + i.tag8, + i.value8, + tci.price, + tci.available, + w.name AS lastName, + w.firstName + FROM tmp.ticketCalculateItem tci + JOIN vn.item i ON i.id = tci.itemFk + JOIN vn.itemType it ON it.id = i.typeFk + JOIN vn.worker w on w.id = it.workerFk + ORDER BY relevancy DESC, itemFk ASC, producer DESC` + ) - 1; + + let pricesIndex = stmts.push( + `SELECT + tcp.itemFk, + tcp.grouping, + tcp.price, + w.name AS warehouse + FROM tmp.ticketComponentPrice tcp + JOIN vn.warehouse w ON w.id = tcp.warehouseFk` + ) - 1; + + stmts.push( + `DROP TEMPORARY TABLE + tmp.item, + tmp.ticketCalculateItem, + tmp.ticketComponentPrice` + ); + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await Self.rawStmt(sql); + + result[itemsIndex].forEach(item => { + result[pricesIndex].forEach(price => { + if (item.id === price.itemFk) { + if (item.prices) { + item.prices.push(price); + } else { + item.prices = [price]; + } + item.available = price.grouping; + } + }); + }); + + return result[itemsIndex]; + }; +}; diff --git a/services/order/common/methods/order/itemFilter.js b/services/order/common/methods/order/itemFilter.js deleted file mode 100644 index d04f3aa92..000000000 --- a/services/order/common/methods/order/itemFilter.js +++ /dev/null @@ -1,61 +0,0 @@ -module.exports = Self => { - Self.remoteMethod('itemFilter', { - description: 'Find all instances of the model matched by filter from the data source.', - accessType: 'READ', - accepts: [ - { - arg: 'filter', - type: 'Object', - description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', - http: {source: 'query'} - } - ], - returns: { - type: ['Object'], - root: true - }, - http: { - path: `/itemFilter`, - verb: 'GET' - } - }); - - Self.itemFilter = async filter => { - let where = filter.where; - let order = await Self.findById(where.id); - - let stmt = `CALL vn2008.bionic_from_type(?, ?, ?, ?); - SELECT bi.*, i.*, w.name AS workerName, w.firstName AS workerFirstName FROM tmp.bionic_item bi - JOIN vn.item i ON i.id = bi.item_id - JOIN vn.itemType it ON it.id = i.typeFk - JOIN vn.worker w on w.id = it.workerFk - ORDER BY relevancy DESC, item_id ASC, producer DESC; - - SELECT pri.*, w.name AS warehouseName FROM tmp.bionic_price pri - JOIN vn.warehouse w ON w.id = pri.warehouse_id;`; - - let [rs, items, prices] = await Self.rawSql(stmt, [ - order.landed, - order.address_id, - order.agency_id, - where.typeFk - ]); - - if (items) { - items.forEach(item => { - prices.forEach(price => { - if (item.item_id === price.item_id) { - if (item.prices) { - item.prices.push(price); - } else { - item.prices = [price]; - } - item.disponible = price.grouping; - } - }); - }); - } - - return items; - }; -}; diff --git a/services/order/common/methods/order/specs/catalogFilter.spec.js b/services/order/common/methods/order/specs/catalogFilter.spec.js new file mode 100644 index 000000000..804d675c6 --- /dev/null +++ b/services/order/common/methods/order/specs/catalogFilter.spec.js @@ -0,0 +1,17 @@ +const app = require(`${servicesDir}/order/server/server`); + +describe('order catalogFilter()', () => { + it('should return an array of items', async() => { + let filter = { + orderFk: 1, + where: { + typeFk: 1 + } + }; + let result = await app.models.Order.catalogFilter(filter); + let firstItemId = result[0].itemFk; + + expect(result.length).toEqual(2); + expect(firstItemId).toEqual(3); + }); +}); diff --git a/services/order/common/methods/order/specs/itemFilter.spec.js b/services/order/common/methods/order/specs/itemFilter.spec.js deleted file mode 100644 index c06972c44..000000000 --- a/services/order/common/methods/order/specs/itemFilter.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -const app = require(`${servicesDir}/order/server/server`); - -describe('order itemFilter()', () => { - it('should call the itemFilter method and return an array of items', async() => { - let filter = { - where: { - id: 1, - typeFk: 1 - } - }; - let result = await app.models.Order.itemFilter(filter); - let firstItemId = result[0].item_id; - - expect(result.length).toEqual(2); - expect(firstItemId).toEqual(3); - }); -}); diff --git a/services/order/common/models/order.js b/services/order/common/models/order.js index cb18ddb1c..cc3dd15be 100644 --- a/services/order/common/models/order.js +++ b/services/order/common/models/order.js @@ -5,5 +5,5 @@ module.exports = Self => { require('../methods/order/getTaxes')(Self); require('../methods/order/isEditable')(Self); require('../methods/order/getTotal')(Self); - require('../methods/order/itemFilter')(Self); + require('../methods/order/catalogFilter')(Self); };