catalog filter #581
This commit is contained in:
parent
46eb1e6f98
commit
300b2c2a07
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="/order/api/Orders/CatalogFilter"
|
||||
filter="::$ctrl.filter"
|
||||
data="items" auto-load="false">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-horizontal>
|
||||
<vn-vertical vn-one>
|
||||
<vn-card>
|
||||
<vn-vertical>
|
||||
<vn-horizontal class="catalog-header" pad-medium>
|
||||
<vn-one>{{model.data.length}} <span translate>results</span></vn-one>
|
||||
<vn-one>-</vn-one>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="catalog-list" pad-small>
|
||||
<section class="product" ng-repeat="item in items">
|
||||
<vn-one>
|
||||
<vn-horizontal>
|
||||
<vn-one class="image">
|
||||
<img
|
||||
ng-src="//verdnatura.es/vn-image-data/catalog/200x200/{{::item.image}}"
|
||||
zoom-image="//verdnatura.es/vn-image-data/catalog/1600x900/{{::item.image}}"
|
||||
on-error-src pointer/>
|
||||
</vn-one>
|
||||
<vn-one pad-small class="description ellipsize">
|
||||
<vn-vertical>
|
||||
<h2 class="ellipsize" vn-tooltip="{{::item.name}}">
|
||||
{{::item.name}}
|
||||
</h2>
|
||||
<span class="ellipsize" vn-tooltip="{{::item.subName}}">
|
||||
{{::item.subName}}
|
||||
</span>
|
||||
|
||||
<vn-label-value
|
||||
label="{{::item.tag5}}"
|
||||
value="{{::item.value5}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::item.tag6}}"
|
||||
value="{{::item.value6}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::item.tag7}}"
|
||||
value="{{::item.value7}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::item.tag8}}"
|
||||
value="{{::item.value8}}">
|
||||
</vn-label-value>
|
||||
|
||||
<vn-horizontal class="price">
|
||||
<vn-one>
|
||||
<span>{{::item.available}}</span>
|
||||
<span translate>from</span>
|
||||
<span>{{::item.price | currency: ' €': 2}}</span>
|
||||
</vn-one>
|
||||
<vn-auto>
|
||||
<a href="" vn-tooltip="Add">
|
||||
<vn-icon icon="add_circle" ng-click="$ctrl.preview($event, item)"></vn-icon>
|
||||
</a>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-one>
|
||||
</section>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-if="model.data.length == 0">
|
||||
<vn-one pad-small translate style="text-align: center">
|
||||
No results
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
</vn-vertical>
|
||||
|
||||
<vn-auto class="right-block">
|
||||
<vn-catalog-filter order="$ctrl.order"></vn-catalog-filter>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-order-prices-popover
|
||||
vn-id="pricesPopover">
|
||||
</vn-order-prices-popover>
|
|
@ -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: {
|
|
@ -1,34 +0,0 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="/order/api/Orders/ItemFilter"
|
||||
filter="::$ctrl.filter"
|
||||
data="items" auto-load="false">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-horizontal>
|
||||
<vn-vertical vn-one>
|
||||
<vn-card>
|
||||
<vn-vertical>
|
||||
<vn-horizontal class="catalogue header" pad-medium>
|
||||
<vn-one>{{model.data.length}} <span translate>results</span></vn-one>
|
||||
<vn-one>-</vn-one>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="catalogue list" pad-small>
|
||||
<vn-order-product
|
||||
ng-repeat="item in items"
|
||||
item="::item">
|
||||
</vn-order-product>
|
||||
<vn-one style="text-align: center" pad-small translate>No results</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
</vn-vertical>
|
||||
|
||||
<vn-auto class="right-block">
|
||||
<vn-filter order="$ctrl.order"></vn-filter>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-order-prices-popover
|
||||
vn-id="pricesPopover">
|
||||
</vn-order-prices-popover>
|
|
@ -1,50 +0,0 @@
|
|||
<vn-one>
|
||||
<vn-horizontal>
|
||||
<vn-one class="image">
|
||||
<img
|
||||
ng-src="//verdnatura.es/vn-image-data/catalog/200x200/{{::$ctrl.item.image}}"
|
||||
zoom-image="//verdnatura.es/vn-image-data/catalog/1600x900/{{::$ctrl.item.image}}"
|
||||
on-error-src pointer/>
|
||||
</vn-one>
|
||||
<vn-one pad-small class="description ellipsize">
|
||||
<vn-vertical>
|
||||
<h2 class="ellipsize" vn-tooltip="{{::$ctrl.item.name}}">
|
||||
{{::$ctrl.item.name}}
|
||||
</h2>
|
||||
<span class="ellipsize" vn-tooltip="{{::$ctrl.item.subName}}">
|
||||
{{::$ctrl.item.subName}}
|
||||
</span>
|
||||
|
||||
<vn-label-value
|
||||
label="{{::$ctrl.item.tag5}}"
|
||||
value="{{::$ctrl.item.value5}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::$ctrl.item.tag6}}"
|
||||
value="{{::$ctrl.item.value6}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::$ctrl.item.tag7}}"
|
||||
value="{{::$ctrl.item.value7}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="{{::$ctrl.item.tag8}}"
|
||||
value="{{::$ctrl.item.value8}}">
|
||||
</vn-label-value>
|
||||
|
||||
<vn-horizontal class="price">
|
||||
<vn-one>
|
||||
<span>{{::$ctrl.item.available}}</span>
|
||||
<span translate>from</span>
|
||||
<span>{{::$ctrl.item.price | currency: ' €': 2}}</span>
|
||||
</vn-one>
|
||||
<vn-auto>
|
||||
<a href="" vn-tooltip="Add">
|
||||
<vn-icon icon="add_circle" ng-click="$ctrl.preview($event)"></vn-icon>
|
||||
</a>
|
||||
</vn-auto>
|
||||
</section>
|
||||
</vn-vertical>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-one>
|
|
@ -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'
|
||||
}
|
||||
});
|
|
@ -1,9 +1,49 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="/order/api/ItemCategories"
|
||||
data="categories">
|
||||
</vn-crud-model>
|
||||
|
||||
|
||||
<vn-horizontal>
|
||||
<vn-vertical vn-one>
|
||||
<vn-card pad-medium>
|
||||
<vn-vertical margin-medium>
|
||||
<vn-submit ng-click="$ctrl.setFilter()" label="Filter"></vn-submit>
|
||||
<vn-card >
|
||||
<vn-vertical>
|
||||
<vn-horizontal pad-medium class="item-category">
|
||||
<vn-one margin-small-v ng-repeat="category in categories">
|
||||
<vn-icon
|
||||
ng-class="{'active': $ctrl.categoryFk == category.id}"
|
||||
pad-small
|
||||
icon="{{::category.icon}}"
|
||||
vn-tooltip="{{::category.name}}"
|
||||
ng-click="$ctrl.categoryFk = category.id">
|
||||
</vn-icon>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-horizontal pad-medium class="catalog-header">
|
||||
<vn-icon icon="search"></vn-icon>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.typeFk"
|
||||
field="$ctrl.typeFk"
|
||||
data="$ctrl.itemTypes"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Type">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-horizontal pad-medium class="catalog-header">
|
||||
<vn-searchbar
|
||||
panel="vn-order-search-panel"
|
||||
on-search="$ctrl.onSearch(filter)"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
</vn-searchbar>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-horizontal pad-medium>
|
||||
tags
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
</vn-vertical>
|
||||
|
|
|
@ -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: '<'
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="order in orders" class="clickable"
|
||||
ui-sref="order.card.catalogue({id: {{::order.id}}})">
|
||||
ui-sref="order.card.catalog({id: {{::order.id}}})">
|
||||
<vn-td>{{::order.id}}</vn-td>
|
||||
<vn-td>{{::order.clientFk}}</vn-td>
|
||||
<vn-td>{{::order.companyFk}}</vn-td>
|
||||
<vn-td>{{::order.created}}</vn-td>
|
||||
<vn-td>{{::order.created | date:'dd/MM/yyyy'}}</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
<vn-empty-rows ng-if="model.data.length === 0" translate>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
value="{{$ctrl.item.name}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Buyer"
|
||||
value="{{$ctrl.item.workerFirstName}} {{$ctrl.item.workerName}}">
|
||||
value="{{$ctrl.item.firstName}} {{$ctrl.item.lastName}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
ng-repeat="tag in $ctrl.tags"
|
||||
|
@ -36,7 +36,7 @@
|
|||
<vn-vertical class="prices">
|
||||
<vn-horizontal
|
||||
ng-repeat="price in $ctrl.prices">
|
||||
<vn-one class="ellipsize text" title="{{::price.warehouseName}}">{{::price.warehouseName}}</vn-one>
|
||||
<vn-one class="ellipsize text" title="{{::price.warehouse}}">{{::price.warehouse}}</vn-one>
|
||||
<vn-one class="number text">
|
||||
<span orange ng-click="$ctrl.addQuantity(price)" class="link">{{::price.grouping}} x </span><span>{{::price.price | currency: ' €': 2}}</span>
|
||||
</vn-one>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<div pad-large style="min-width: 10em">
|
||||
<form ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Tag"
|
||||
field="filter['itg.tagFk']"
|
||||
url="/api/Tags"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
<tpl-item>{{name}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Value"
|
||||
model="filter['itg.value']">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal margin-large-top>
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
|
@ -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
|
||||
});
|
|
@ -3,30 +3,30 @@
|
|||
|
||||
|
||||
@media screen and (max-width: 1920px){
|
||||
vn-order-product {
|
||||
.catalog-list .product {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1800px){
|
||||
vn-order-product {
|
||||
.catalog-list .product {
|
||||
width: 33.33%
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1600px){
|
||||
vn-order-product {
|
||||
.catalog-list .product {
|
||||
width: 50%
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px){
|
||||
vn-order-product {
|
||||
.catalog-list .product {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.catalogue.header {
|
||||
.catalog-header {
|
||||
border-color: $lines;
|
||||
border-bottom: 1px solid rgba($lines, 0.5);
|
||||
|
||||
|
@ -35,12 +35,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.catalogue.list {
|
||||
.catalog-list {
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap
|
||||
}
|
||||
vn-order-product {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.product {
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
|
||||
|
@ -108,3 +108,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
<vn-card pad-large>
|
||||
<vn-vertical>
|
||||
<vn-title>Pictures</vn-title>
|
||||
<vn-horizontal class="catalogue list" pad-small>
|
||||
<vn-order-product ng-repeat="sale in sales">
|
||||
<vn-horizontal class="catalog-list" pad-small>
|
||||
<section class="product" ng-repeat="sale in sales">
|
||||
<vn-one>
|
||||
<vn-horizontal>
|
||||
<vn-one class="image">
|
||||
|
@ -54,12 +54,12 @@
|
|||
<span translate>by</span>
|
||||
<span>{{::sale.price | currency: ' €': 2}}</span>
|
||||
</vn-one>
|
||||
</section>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-one>
|
||||
</vn-order-product>
|
||||
</section>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-if="model.data.length == 0">
|
||||
<vn-one pad-small-v translate>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -17,5 +17,3 @@ VIEW `sample` AS
|
|||
`e`.`hasCompany` AS `hasCompany`
|
||||
FROM
|
||||
`vn2008`.`escritos` `e`;
|
||||
|
||||
DROP VIEW `vn`.`clientNotificationType`;
|
|
@ -14,5 +14,3 @@ VIEW `clientSample` AS
|
|||
`e`.`empresa_id` AS `companyFk`
|
||||
FROM
|
||||
`vn2008`.`escritos_det` `e`;
|
||||
|
||||
DROP VIEW `vn`.`clientNotification`;
|
|
@ -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;
|
|
@ -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`;
|
|
@ -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 ;
|
||||
|
|
@ -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"
|
||||
}
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,11 @@
|
|||
"type": "belongsTo",
|
||||
"model": "Warehouse",
|
||||
"foreignKey": "warehouseFk"
|
||||
},
|
||||
"category": {
|
||||
"type": "belongsTo",
|
||||
"model": "ItemCategory",
|
||||
"foreignKey": "categoryFk"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -90,6 +90,9 @@
|
|||
"ItemType": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ItemCategory": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Expence": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue