Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2820-add_item_scopeDays
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
4cedd53bce
|
@ -1,11 +1,51 @@
|
|||
const fs = require('fs-extra');
|
||||
const sharp = require('sharp');
|
||||
const path = require('path');
|
||||
const readChunk = require('read-chunk');
|
||||
const imageType = require('image-type');
|
||||
const bmp = require('bmp-js');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/image/download')(Self);
|
||||
require('../methods/image/upload')(Self);
|
||||
|
||||
// Function extracted from jimp package (utils)
|
||||
function scan(image, x, y, w, h, f) {
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
|
||||
for (let _y = y; _y < y + h; _y++) {
|
||||
for (let _x = x; _x < x + w; _x++) {
|
||||
const idx = (image.bitmap.width * _y + _x) << 2;
|
||||
f.call(image, _x, _y, idx);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
// Function extracted from jimp package (type-bmp)
|
||||
function fromAGBR(bitmap) {
|
||||
return scan({bitmap}, 0, 0, bitmap.width, bitmap.height, function(
|
||||
x,
|
||||
y,
|
||||
index
|
||||
) {
|
||||
const alpha = this.bitmap.data[index + 0];
|
||||
const blue = this.bitmap.data[index + 1];
|
||||
const green = this.bitmap.data[index + 2];
|
||||
const red = this.bitmap.data[index + 3];
|
||||
|
||||
this.bitmap.data[index + 0] = red;
|
||||
this.bitmap.data[index + 1] = green;
|
||||
this.bitmap.data[index + 2] = blue;
|
||||
this.bitmap.data[index + 3] = bitmap.is_with_alpha ? alpha : 0xff;
|
||||
}).bitmap;
|
||||
}
|
||||
|
||||
Self.registerImage = async(collectionName, srcFilePath, fileName, entityId) => {
|
||||
const models = Self.app.models;
|
||||
const tx = await Self.beginTransaction({});
|
||||
|
@ -48,13 +88,31 @@ module.exports = Self => {
|
|||
const dstDir = path.join(collectionDir, 'full');
|
||||
const dstFile = path.join(dstDir, file);
|
||||
|
||||
const buffer = readChunk.sync(srcFilePath, 0, 12);
|
||||
const type = imageType(buffer);
|
||||
|
||||
let sharpOptions;
|
||||
let imgSrc = srcFilePath;
|
||||
if (type.mime == 'image/bmp') {
|
||||
const bmpBuffer = fs.readFileSync(srcFilePath);
|
||||
const bmpData = fromAGBR(bmp.decode(bmpBuffer));
|
||||
imgSrc = bmpData.data;
|
||||
sharpOptions = {
|
||||
raw: {
|
||||
width: bmpData.width,
|
||||
height: bmpData.height,
|
||||
channels: 4
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const resizeOpts = {
|
||||
withoutEnlargement: true,
|
||||
fit: 'inside'
|
||||
};
|
||||
|
||||
await fs.mkdir(dstDir, {recursive: true});
|
||||
await sharp(srcFilePath, {failOnError: false})
|
||||
await sharp(imgSrc, sharpOptions)
|
||||
.resize(collection.maxWidth, collection.maxHeight, resizeOpts)
|
||||
.png()
|
||||
.toFile(dstFile);
|
||||
|
@ -69,7 +127,7 @@ module.exports = Self => {
|
|||
};
|
||||
|
||||
await fs.mkdir(dstDir, {recursive: true});
|
||||
await sharp(srcFilePath, {failOnError: false})
|
||||
await sharp(imgSrc, sharpOptions)
|
||||
.resize(size.width, size.height, resizeOpts)
|
||||
.png()
|
||||
.toFile(dstFile);
|
||||
|
|
|
@ -78,6 +78,8 @@ module.exports = Self => {
|
|||
return {'ic.id': value};
|
||||
case 'salesPersonFk':
|
||||
return {'it.workerFk': value};
|
||||
case 'code':
|
||||
return {'it.code': value};
|
||||
case 'typeFk':
|
||||
return {'i.typeFk': value};
|
||||
case 'active':
|
||||
|
@ -103,6 +105,7 @@ module.exports = Self => {
|
|||
i.id AS itemFk,
|
||||
i.size,
|
||||
i.density,
|
||||
it.code,
|
||||
i.typeFk,
|
||||
i.family,
|
||||
i.isActive,
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
<vn-th field="quantity">Quantity</vn-th>
|
||||
<vn-th field="description" style="text-align: center">Description</vn-th>
|
||||
<vn-th field="size">Size</vn-th>
|
||||
<vn-th field="tags" style="text-align: center">Tags</vn-th>
|
||||
<vn-th field="type">Type</vn-th>
|
||||
<vn-th field="name" style="text-align: center">Tags</vn-th>
|
||||
<vn-th field="code">Type</vn-th>
|
||||
<vn-th field="intrastat">Intrastat</vn-th>
|
||||
<vn-th field="origin">Origin</vn-th>
|
||||
<vn-th field="density">Density</vn-th>
|
||||
|
@ -109,7 +109,7 @@
|
|||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::buy.type}}">
|
||||
{{::buy.type}}
|
||||
{{::buy.code}}
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::item.intrastat}}">
|
||||
{{::buy.intrastat}}
|
||||
|
|
|
@ -46,13 +46,8 @@ module.exports = Self => {
|
|||
|
||||
if (!image) return;
|
||||
|
||||
const srcFile = image.url.split('/').pop();
|
||||
const dotIndex = srcFile.lastIndexOf('.');
|
||||
|
||||
let fileName = srcFile.substring(0, dotIndex);
|
||||
if (dotIndex == -1)
|
||||
fileName = srcFile;
|
||||
|
||||
const srcFile = image.url;
|
||||
const fileName = srcFile.replace(/\.|\/|:|\?|\\|=|%/g, '');
|
||||
const file = `${fileName}.png`;
|
||||
const filePath = path.join(tempPath, file);
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ module.exports = Self => {
|
|||
arg: 'description',
|
||||
type: 'String',
|
||||
description: 'The item description',
|
||||
}, {
|
||||
arg: 'stemMultiplier',
|
||||
type: 'Integer',
|
||||
description: 'The item multiplier',
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
|
@ -80,16 +84,22 @@ module.exports = Self => {
|
|||
: {or: [{'i.name': {like: `%${value}%`}}, codeWhere]};
|
||||
case 'id':
|
||||
return {'i.id': value};
|
||||
case 'description':
|
||||
return {'i.description': {like: `%${value}%`}};
|
||||
case 'categoryFk':
|
||||
return {'ic.id': value};
|
||||
case 'salesPersonFk':
|
||||
return {'t.workerFk': value};
|
||||
case 'typeFk':
|
||||
return {'i.typeFk': value};
|
||||
case 'isActive':
|
||||
return {'i.isActive': value};
|
||||
case 'multiplier':
|
||||
return {'i.stemMultiplier': value};
|
||||
case 'typeFk':
|
||||
return {'i.typeFk': value};
|
||||
case 'category':
|
||||
return {'ic.name': value};
|
||||
case 'salesPersonFk':
|
||||
return {'it.workerFk': value};
|
||||
case 'origin':
|
||||
return {'ori.code': value};
|
||||
case 'niche':
|
||||
return {'ip.code': value};
|
||||
case 'intrastat':
|
||||
return {'intr.description': value};
|
||||
}
|
||||
});
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
@ -98,7 +108,8 @@ module.exports = Self => {
|
|||
let stmt;
|
||||
|
||||
stmt = new ParameterizedSQL(
|
||||
`SELECT i.id,
|
||||
`SELECT
|
||||
i.id,
|
||||
i.image,
|
||||
i.name,
|
||||
i.description,
|
||||
|
@ -111,29 +122,30 @@ module.exports = Self => {
|
|||
i.tag10, i.value10,
|
||||
i.subName,
|
||||
i.isActive,
|
||||
t.name type,
|
||||
t.workerFk buyerFk,
|
||||
u.name userName,
|
||||
intr.description AS intrastat,
|
||||
i.stems,
|
||||
ori.code AS origin,
|
||||
ic.name AS category,
|
||||
i.density,
|
||||
i.stemMultiplier,
|
||||
i.typeFk,
|
||||
it.name AS typeName,
|
||||
it.workerFk AS buyerFk,
|
||||
u.name AS userName,
|
||||
ori.code AS origin,
|
||||
ic.name AS category,
|
||||
intr.description AS intrastat,
|
||||
b.grouping,
|
||||
b.packing,
|
||||
itn.code AS niche, @visibleCalc
|
||||
ip.code AS niche, @visibleCalc
|
||||
FROM item i
|
||||
LEFT JOIN itemType t ON t.id = i.typeFk
|
||||
LEFT JOIN itemCategory ic ON ic.id = t.categoryFk
|
||||
LEFT JOIN worker w ON w.id = t.workerFk
|
||||
LEFT JOIN itemType it ON it.id = i.typeFk
|
||||
LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||
LEFT JOIN worker w ON w.id = it.workerFk
|
||||
LEFT JOIN account.user u ON u.id = w.userFk
|
||||
LEFT JOIN intrastat intr ON intr.id = i.intrastatFk
|
||||
LEFT JOIN producer pr ON pr.id = i.producerFk
|
||||
LEFT JOIN origin ori ON ori.id = i.originFk
|
||||
LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = t.warehouseFk
|
||||
LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = it.warehouseFk
|
||||
LEFT JOIN vn.buy b ON b.id = lb.buy_id
|
||||
LEFT JOIN itemPlacement itn ON itn.itemFk = i.id AND itn.warehouseFk = t.warehouseFk`
|
||||
LEFT JOIN itemPlacement ip ON ip.itemFk = i.id AND ip.warehouseFk = it.warehouseFk`
|
||||
);
|
||||
|
||||
if (ctx.args.tags) {
|
||||
|
|
|
@ -113,6 +113,12 @@
|
|||
ng-model="$ctrl.item.stems"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
vn-one
|
||||
min="0"
|
||||
label="Multiplier"
|
||||
ng-model="$ctrl.item.stemMultiplier">
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
|
|
|
@ -9,4 +9,5 @@ Price in kg: Precio en kg
|
|||
New intrastat: Nuevo intrastat
|
||||
Identifier: Identificador
|
||||
Fragile: Frágil
|
||||
Is shown at website, app that this item cannot travel (wreath, palms, ...): Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
|
||||
Is shown at website, app that this item cannot travel (wreath, palms, ...): Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
|
||||
Multiplier: Multiplicador
|
|
@ -5,107 +5,107 @@
|
|||
model="model"
|
||||
class="vn-w-xl vn-mb-xl">
|
||||
<vn-card>
|
||||
<vn-table
|
||||
model="model"
|
||||
show-fields="$ctrl.showFields"
|
||||
vn-smart-table="itemIndex">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th field="id" shrink>Id</vn-th>
|
||||
<vn-th field="grouping" shrink>Grouping</vn-th>
|
||||
<vn-th field="packing" shrink>Packing</vn-th>
|
||||
<vn-th field="description">Description</vn-th>
|
||||
<vn-th field="stems" shrink>Stems</vn-th>
|
||||
<vn-th field="size" shrink>Size</vn-th>
|
||||
<vn-th field="niche" shrink>Niche</vn-th>
|
||||
<vn-th field="type" shrink>Type</vn-th>
|
||||
<vn-th field="category" shrink>Category</vn-th>
|
||||
<vn-th field="intrastat" shrink>Intrastat</vn-th>
|
||||
<vn-th field="origin" shrink>Origin</vn-th>
|
||||
<vn-th field="salesperson" shrink>Buyer</vn-th>
|
||||
<vn-th field="density" shrink>Density</vn-th>
|
||||
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
||||
<vn-th field="active" shrink>Active</vn-th>
|
||||
<vn-th></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="item in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="item.card.summary({id: item.id})">
|
||||
<vn-td shrink>
|
||||
<img
|
||||
ng-src="{{::$root.imagePath('catalog', '50x50', item.id)}}"
|
||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
||||
vn-click-stop
|
||||
on-error-src/>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<span
|
||||
vn-click-stop="itemDescriptor.show($event, item.id)"
|
||||
class="link">
|
||||
{{::item.id}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.grouping | dashIfEmpty}}</vn-td>
|
||||
<vn-td shrink>{{::item.packing | dashIfEmpty}}</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<vn-one title="{{::item.name}}">{{::item.name}}</vn-one>
|
||||
<vn-one ng-if="::item.subName">
|
||||
<h3 title="{{::item.subName}}">{{::item.subName}}</h3>
|
||||
</vn-one>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.stems}}</vn-td>
|
||||
<vn-td shrink>{{::item.size}}</vn-td>
|
||||
<vn-td shrink>{{::item.niche}}</vn-td>
|
||||
<vn-td shrink title="{{::item.type}}">
|
||||
{{::item.type}}
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::item.category}}">
|
||||
{{::item.category}}
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::item.intrastat}}">
|
||||
{{::item.intrastat}}
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.origin}}</vn-td>
|
||||
<vn-td shrink title="{{::item.userName}}">
|
||||
<span
|
||||
class="link"
|
||||
vn-click-stop="workerDescriptor.show($event, item.buyerFk)">
|
||||
{{::item.userName}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.density}}</vn-td>
|
||||
<vn-td shrink >{{::item.stemMultiplier}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-check
|
||||
disabled="true"
|
||||
ng-model="::item.isActive">
|
||||
</vn-check>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-horizontal class="buttons">
|
||||
<vn-icon-button
|
||||
vn-click-stop="clone.show(item.id)"
|
||||
vn-tooltip="Clone"
|
||||
icon="icon-clone">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.preview(item)"
|
||||
vn-tooltip="Preview"
|
||||
icon="preview">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
<vn-table
|
||||
model="model"
|
||||
show-fields="$ctrl.showFields"
|
||||
vn-smart-table="itemIndex">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th field="id" shrink>Id</vn-th>
|
||||
<vn-th field="grouping" shrink>Grouping</vn-th>
|
||||
<vn-th field="packing" shrink>Packing</vn-th>
|
||||
<vn-th field="name">Description</vn-th>
|
||||
<vn-th field="stems" shrink>Stems</vn-th>
|
||||
<vn-th field="size" shrink>Size</vn-th>
|
||||
<vn-th field="niche" shrink>Niche</vn-th>
|
||||
<vn-th field="typeFk" shrink>Type</vn-th>
|
||||
<vn-th field="category" shrink>Category</vn-th>
|
||||
<vn-th field="intrastat" shrink>Intrastat</vn-th>
|
||||
<vn-th field="origin" shrink>Origin</vn-th>
|
||||
<vn-th field="salesperson" shrink>Buyer</vn-th>
|
||||
<vn-th field="density" shrink>Density</vn-th>
|
||||
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
||||
<vn-th field="active" shrink>Active</vn-th>
|
||||
<vn-th></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="item in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="item.card.summary({id: item.id})">
|
||||
<vn-td shrink>
|
||||
<img
|
||||
ng-src="{{::$root.imagePath('catalog', '50x50', item.id)}}"
|
||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
||||
vn-click-stop
|
||||
on-error-src/>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<span
|
||||
vn-click-stop="itemDescriptor.show($event, item.id)"
|
||||
class="link">
|
||||
{{::item.id}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.grouping | dashIfEmpty}}</vn-td>
|
||||
<vn-td shrink>{{::item.packing | dashIfEmpty}}</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<vn-one title="{{::item.name}}">{{::item.name}}</vn-one>
|
||||
<vn-one ng-if="::item.subName">
|
||||
<h3 title="{{::item.subName}}">{{::item.subName}}</h3>
|
||||
</vn-one>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.stems}}</vn-td>
|
||||
<vn-td shrink>{{::item.size}}</vn-td>
|
||||
<vn-td shrink>{{::item.niche}}</vn-td>
|
||||
<vn-td shrink title="{{::item.typeName}}">
|
||||
{{::item.typeName}}
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::item.category}}">
|
||||
{{::item.category}}
|
||||
</vn-td>
|
||||
<vn-td shrink title="{{::item.intrastat}}">
|
||||
{{::item.intrastat}}
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.origin}}</vn-td>
|
||||
<vn-td shrink title="{{::item.userName}}">
|
||||
<span
|
||||
class="link"
|
||||
vn-click-stop="workerDescriptor.show($event, item.buyerFk)">
|
||||
{{::item.userName}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::item.density}}</vn-td>
|
||||
<vn-td shrink >{{::item.stemMultiplier}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-check
|
||||
disabled="true"
|
||||
ng-model="::item.isActive">
|
||||
</vn-check>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-horizontal class="buttons">
|
||||
<vn-icon-button
|
||||
vn-click-stop="clone.show(item.id)"
|
||||
vn-tooltip="Clone"
|
||||
icon="icon-clone">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.preview(item)"
|
||||
vn-tooltip="Preview"
|
||||
icon="preview">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
|
||||
|
@ -127,4 +127,31 @@
|
|||
<vn-item-summary
|
||||
item="$ctrl.itemSelected">
|
||||
</vn-item-summary>
|
||||
</vn-popup>
|
||||
</vn-popup>
|
||||
<vn-contextmenu
|
||||
vn-id="contextmenu"
|
||||
targets="['vn-data-viewer']"
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-menu>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.filterBySelection()">
|
||||
Filter by selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.excludeSelection()">
|
||||
Exclude selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.removeFilter()">
|
||||
Remove filter
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-click="contextmenu.removeAllFilters()">
|
||||
Remove all filters
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
</vn-contextmenu>
|
|
@ -11,6 +11,36 @@ class Controller extends Section {
|
|||
};
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'category':
|
||||
return {'ic.name': value};
|
||||
case 'salesPersonFk':
|
||||
return {'it.workerFk': value};
|
||||
case 'grouping':
|
||||
return {'b.grouping': value};
|
||||
case 'packing':
|
||||
return {'b.packing': value};
|
||||
case 'origin':
|
||||
return {'ori.code': value};
|
||||
case 'niche':
|
||||
return {'ip.code': value};
|
||||
case 'typeFk':
|
||||
return {'i.typeFk': value};
|
||||
case 'intrastat':
|
||||
return {'intr.description': value};
|
||||
case 'id':
|
||||
case 'size':
|
||||
case 'name':
|
||||
case 'subname':
|
||||
case 'isActive':
|
||||
case 'density':
|
||||
case 'stemMultiplier':
|
||||
case 'stems':
|
||||
return {[`i.${param}`]: value};
|
||||
}
|
||||
}
|
||||
|
||||
onCloneAccept(itemFk) {
|
||||
return this.$http.post(`Items/${itemFk}/clone`)
|
||||
.then(res => {
|
||||
|
|
|
@ -55,6 +55,9 @@
|
|||
<vn-label-value label="stems"
|
||||
value="{{$ctrl.summary.item.stems}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Multiplier"
|
||||
value="{{$ctrl.summary.item.stemMultiplier}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Buyer">
|
||||
<span
|
||||
ng-click="workerDescriptor.show($event, $ctrl.summary.item.itemType.worker.userFk)"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('getAverageDays', {
|
||||
description: 'Returns the average days duration and the two warehouses of the travel.',
|
||||
|
@ -8,7 +9,7 @@ module.exports = Self => {
|
|||
required: true
|
||||
}],
|
||||
returns: {
|
||||
type: 'number',
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
@ -18,15 +19,48 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.getAverageDays = async agencyModeFk => {
|
||||
const query = `
|
||||
SELECT t.id, t.warehouseInFk, t.warehouseOutFk,
|
||||
(SELECT ROUND(AVG(DATEDIFF(t.landed, t.shipped )))
|
||||
FROM travel t
|
||||
WHERE t.agencyFk = ? LIMIT 50) AS dayDuration
|
||||
FROM travel t
|
||||
WHERE t.agencyFk = ? ORDER BY t.id DESC LIMIT 1;`;
|
||||
const conn = Self.dataSource.connector;
|
||||
let stmts = [];
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.travel');
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
CREATE TEMPORARY TABLE tmp.travel (
|
||||
SELECT
|
||||
t.id,
|
||||
t.warehouseInFk,
|
||||
t.warehouseOutFk,
|
||||
t.landed,
|
||||
t.shipped,
|
||||
t.agencyFk
|
||||
FROM travel t
|
||||
WHERE t.agencyFk = ? LIMIT 50)`, [agencyModeFk]);
|
||||
stmts.push(stmt);
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
t.id,
|
||||
t.warehouseInFk,
|
||||
t.warehouseOutFk,
|
||||
(SELECT ROUND(AVG(DATEDIFF(t.landed, t.shipped )))
|
||||
FROM tmp.travel t
|
||||
WHERE t.agencyFk
|
||||
ORDER BY id DESC LIMIT 50) AS dayDuration
|
||||
FROM tmp.travel t
|
||||
WHERE t.agencyFk
|
||||
ORDER BY t.id DESC LIMIT 1`);
|
||||
|
||||
const avgDaysIndex = stmts.push(stmt) - 1;
|
||||
|
||||
stmts.push(
|
||||
`DROP TEMPORARY TABLE
|
||||
tmp.travel`);
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql);
|
||||
|
||||
const [avgDays] = result[avgDaysIndex];
|
||||
|
||||
const [avgDays] = await Self.rawSql(query, [agencyModeFk, agencyModeFk]);
|
||||
return avgDays;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -22,6 +22,9 @@ class Controller extends Section {
|
|||
agencyModeFk: this.travel.agencyModeFk
|
||||
};
|
||||
this.$http.get(query, {params}).then(res => {
|
||||
if (!res.data)
|
||||
return;
|
||||
|
||||
const landed = new Date(value);
|
||||
const futureDate = landed.getDate() + res.data.dayDuration;
|
||||
landed.setDate(futureDate);
|
||||
|
|
|
@ -51,14 +51,29 @@ describe('Travel Component vnTravelCreate', () => {
|
|||
expect(controller.travel.warehouseOutFk).toBeUndefined();
|
||||
});
|
||||
|
||||
it(`should do nothing if there's no response data.`, () => {
|
||||
controller.travel = {agencyModeFk: 4};
|
||||
const tomorrow = new Date();
|
||||
|
||||
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
|
||||
$httpBackend.expectGET(query).respond(undefined);
|
||||
controller.onShippedChange(tomorrow);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.travel.warehouseInFk).toBeUndefined();
|
||||
expect(controller.travel.warehouseOutFk).toBeUndefined();
|
||||
expect(controller.travel.dayDuration).toBeUndefined();
|
||||
});
|
||||
|
||||
it(`should fill the fields when it's selected a date and agency.`, () => {
|
||||
controller.travel = {agencyModeFk: 1};
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
tomorrow.setDate(tomorrow.getDate() + 9);
|
||||
const expectedResponse = {
|
||||
dayDuration: 2,
|
||||
warehouseInFk: 1,
|
||||
warehouseOutFk: 2
|
||||
id: 8,
|
||||
dayDuration: 9,
|
||||
warehouseInFk: 5,
|
||||
warehouseOutFk: 1
|
||||
};
|
||||
|
||||
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
|
||||
|
|
|
@ -42,6 +42,11 @@
|
|||
"model": "Account",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"boss": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"foreignKey": "bossFk"
|
||||
},
|
||||
"client": {
|
||||
"type": "belongsTo",
|
||||
"model": "Client",
|
||||
|
|
|
@ -29,6 +29,15 @@
|
|||
ng-model="$ctrl.worker.phone"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
disabled="false"
|
||||
ng-model="$ctrl.worker.bossFk"
|
||||
url="Clients/activeWorkersWithRole"
|
||||
show-field="nickname"
|
||||
search-function="{firstName: $search}"
|
||||
where="{role: 'employee'}"
|
||||
label="Boss">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
|
|
|
@ -7,6 +7,7 @@ Extension: Extensión
|
|||
Fiscal identifier: NIF
|
||||
Go to client: Ir al cliente
|
||||
Last name: Apellidos
|
||||
Boss: Jefe
|
||||
Log: Historial
|
||||
Private Branch Exchange: Centralita
|
||||
Role: Rol
|
||||
|
|
|
@ -30,6 +30,14 @@
|
|||
<vn-label-value label="Department"
|
||||
value="{{worker.department.department.name}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Boss">
|
||||
<span
|
||||
ng-click="workerDescriptor.show($event, worker.boss.id)"
|
||||
class="link">
|
||||
{{::worker.boss.nickname}}
|
||||
</span>
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Phone"
|
||||
value="{{worker.phone}}">
|
||||
</vn-label-value>
|
||||
|
@ -50,4 +58,7 @@
|
|||
</vn-label-value>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
</vn-card>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
|
@ -11,8 +11,8 @@ class Controller extends Summary {
|
|||
this.$.worker = null;
|
||||
if (!value) return;
|
||||
|
||||
let query = `Workers/${value.id}`;
|
||||
let filter = {
|
||||
const query = `Workers/${value.id}`;
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
|
@ -31,13 +31,20 @@ class Controller extends Summary {
|
|||
}
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
relation: 'client',
|
||||
scope: {fields: ['fi']}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
relation: 'boss',
|
||||
scope: {fields: ['id', 'nickname']}
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
scope: {fields: ['extension']}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,10 +12,12 @@
|
|||
"node": ">=12"
|
||||
},
|
||||
"dependencies": {
|
||||
"bmp-js": "^0.1.0",
|
||||
"compression": "^1.7.3",
|
||||
"fs-extra": "^5.0.0",
|
||||
"helmet": "^3.21.2",
|
||||
"i18n": "^0.8.4",
|
||||
"image-type": "^4.1.0",
|
||||
"imap": "^0.8.19",
|
||||
"ldapjs": "^2.2.0",
|
||||
"loopback": "^3.26.0",
|
||||
|
@ -30,6 +32,7 @@
|
|||
"node-ssh": "^11.0.0",
|
||||
"object-diff": "0.0.4",
|
||||
"object.pick": "^1.3.0",
|
||||
"read-chunk": "^3.2.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise-native": "^1.0.8",
|
||||
"require-yaml": "0.0.1",
|
||||
|
|
Loading…
Reference in New Issue