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 fs = require('fs-extra');
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const readChunk = require('read-chunk');
|
||||||
|
const imageType = require('image-type');
|
||||||
|
const bmp = require('bmp-js');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/image/download')(Self);
|
require('../methods/image/download')(Self);
|
||||||
require('../methods/image/upload')(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) => {
|
Self.registerImage = async(collectionName, srcFilePath, fileName, entityId) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const tx = await Self.beginTransaction({});
|
const tx = await Self.beginTransaction({});
|
||||||
|
@ -48,13 +88,31 @@ module.exports = Self => {
|
||||||
const dstDir = path.join(collectionDir, 'full');
|
const dstDir = path.join(collectionDir, 'full');
|
||||||
const dstFile = path.join(dstDir, file);
|
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 = {
|
const resizeOpts = {
|
||||||
withoutEnlargement: true,
|
withoutEnlargement: true,
|
||||||
fit: 'inside'
|
fit: 'inside'
|
||||||
};
|
};
|
||||||
|
|
||||||
await fs.mkdir(dstDir, {recursive: true});
|
await fs.mkdir(dstDir, {recursive: true});
|
||||||
await sharp(srcFilePath, {failOnError: false})
|
await sharp(imgSrc, sharpOptions)
|
||||||
.resize(collection.maxWidth, collection.maxHeight, resizeOpts)
|
.resize(collection.maxWidth, collection.maxHeight, resizeOpts)
|
||||||
.png()
|
.png()
|
||||||
.toFile(dstFile);
|
.toFile(dstFile);
|
||||||
|
@ -69,7 +127,7 @@ module.exports = Self => {
|
||||||
};
|
};
|
||||||
|
|
||||||
await fs.mkdir(dstDir, {recursive: true});
|
await fs.mkdir(dstDir, {recursive: true});
|
||||||
await sharp(srcFilePath, {failOnError: false})
|
await sharp(imgSrc, sharpOptions)
|
||||||
.resize(size.width, size.height, resizeOpts)
|
.resize(size.width, size.height, resizeOpts)
|
||||||
.png()
|
.png()
|
||||||
.toFile(dstFile);
|
.toFile(dstFile);
|
||||||
|
|
|
@ -78,6 +78,8 @@ module.exports = Self => {
|
||||||
return {'ic.id': value};
|
return {'ic.id': value};
|
||||||
case 'salesPersonFk':
|
case 'salesPersonFk':
|
||||||
return {'it.workerFk': value};
|
return {'it.workerFk': value};
|
||||||
|
case 'code':
|
||||||
|
return {'it.code': value};
|
||||||
case 'typeFk':
|
case 'typeFk':
|
||||||
return {'i.typeFk': value};
|
return {'i.typeFk': value};
|
||||||
case 'active':
|
case 'active':
|
||||||
|
@ -103,6 +105,7 @@ module.exports = Self => {
|
||||||
i.id AS itemFk,
|
i.id AS itemFk,
|
||||||
i.size,
|
i.size,
|
||||||
i.density,
|
i.density,
|
||||||
|
it.code,
|
||||||
i.typeFk,
|
i.typeFk,
|
||||||
i.family,
|
i.family,
|
||||||
i.isActive,
|
i.isActive,
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
<vn-th field="quantity">Quantity</vn-th>
|
<vn-th field="quantity">Quantity</vn-th>
|
||||||
<vn-th field="description" style="text-align: center">Description</vn-th>
|
<vn-th field="description" style="text-align: center">Description</vn-th>
|
||||||
<vn-th field="size">Size</vn-th>
|
<vn-th field="size">Size</vn-th>
|
||||||
<vn-th field="tags" style="text-align: center">Tags</vn-th>
|
<vn-th field="name" style="text-align: center">Tags</vn-th>
|
||||||
<vn-th field="type">Type</vn-th>
|
<vn-th field="code">Type</vn-th>
|
||||||
<vn-th field="intrastat">Intrastat</vn-th>
|
<vn-th field="intrastat">Intrastat</vn-th>
|
||||||
<vn-th field="origin">Origin</vn-th>
|
<vn-th field="origin">Origin</vn-th>
|
||||||
<vn-th field="density">Density</vn-th>
|
<vn-th field="density">Density</vn-th>
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
</vn-fetched-tags>
|
</vn-fetched-tags>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink title="{{::buy.type}}">
|
<vn-td shrink title="{{::buy.type}}">
|
||||||
{{::buy.type}}
|
{{::buy.code}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink title="{{::item.intrastat}}">
|
<vn-td shrink title="{{::item.intrastat}}">
|
||||||
{{::buy.intrastat}}
|
{{::buy.intrastat}}
|
||||||
|
|
|
@ -46,13 +46,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (!image) return;
|
if (!image) return;
|
||||||
|
|
||||||
const srcFile = image.url.split('/').pop();
|
const srcFile = image.url;
|
||||||
const dotIndex = srcFile.lastIndexOf('.');
|
const fileName = srcFile.replace(/\.|\/|:|\?|\\|=|%/g, '');
|
||||||
|
|
||||||
let fileName = srcFile.substring(0, dotIndex);
|
|
||||||
if (dotIndex == -1)
|
|
||||||
fileName = srcFile;
|
|
||||||
|
|
||||||
const file = `${fileName}.png`;
|
const file = `${fileName}.png`;
|
||||||
const filePath = path.join(tempPath, file);
|
const filePath = path.join(tempPath, file);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ module.exports = Self => {
|
||||||
arg: 'description',
|
arg: 'description',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The item description',
|
description: 'The item description',
|
||||||
|
}, {
|
||||||
|
arg: 'stemMultiplier',
|
||||||
|
type: 'Integer',
|
||||||
|
description: 'The item multiplier',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -80,16 +84,22 @@ module.exports = Self => {
|
||||||
: {or: [{'i.name': {like: `%${value}%`}}, codeWhere]};
|
: {or: [{'i.name': {like: `%${value}%`}}, codeWhere]};
|
||||||
case 'id':
|
case 'id':
|
||||||
return {'i.id': value};
|
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':
|
case 'isActive':
|
||||||
return {'i.isActive': value};
|
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});
|
filter = mergeFilters(filter, {where});
|
||||||
|
@ -98,7 +108,8 @@ module.exports = Self => {
|
||||||
let stmt;
|
let stmt;
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(
|
stmt = new ParameterizedSQL(
|
||||||
`SELECT i.id,
|
`SELECT
|
||||||
|
i.id,
|
||||||
i.image,
|
i.image,
|
||||||
i.name,
|
i.name,
|
||||||
i.description,
|
i.description,
|
||||||
|
@ -111,29 +122,30 @@ module.exports = Self => {
|
||||||
i.tag10, i.value10,
|
i.tag10, i.value10,
|
||||||
i.subName,
|
i.subName,
|
||||||
i.isActive,
|
i.isActive,
|
||||||
t.name type,
|
|
||||||
t.workerFk buyerFk,
|
|
||||||
u.name userName,
|
|
||||||
intr.description AS intrastat,
|
|
||||||
i.stems,
|
i.stems,
|
||||||
ori.code AS origin,
|
|
||||||
ic.name AS category,
|
|
||||||
i.density,
|
i.density,
|
||||||
i.stemMultiplier,
|
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.grouping,
|
||||||
b.packing,
|
b.packing,
|
||||||
itn.code AS niche, @visibleCalc
|
ip.code AS niche, @visibleCalc
|
||||||
FROM item i
|
FROM item i
|
||||||
LEFT JOIN itemType t ON t.id = i.typeFk
|
LEFT JOIN itemType it ON it.id = i.typeFk
|
||||||
LEFT JOIN itemCategory ic ON ic.id = t.categoryFk
|
LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||||
LEFT JOIN worker w ON w.id = t.workerFk
|
LEFT JOIN worker w ON w.id = it.workerFk
|
||||||
LEFT JOIN account.user u ON u.id = w.userFk
|
LEFT JOIN account.user u ON u.id = w.userFk
|
||||||
LEFT JOIN intrastat intr ON intr.id = i.intrastatFk
|
LEFT JOIN intrastat intr ON intr.id = i.intrastatFk
|
||||||
LEFT JOIN producer pr ON pr.id = i.producerFk
|
LEFT JOIN producer pr ON pr.id = i.producerFk
|
||||||
LEFT JOIN origin ori ON ori.id = i.originFk
|
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 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) {
|
if (ctx.args.tags) {
|
||||||
|
|
|
@ -113,6 +113,12 @@
|
||||||
ng-model="$ctrl.item.stems"
|
ng-model="$ctrl.item.stems"
|
||||||
rule>
|
rule>
|
||||||
</vn-input-number>
|
</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-horizontal>
|
<vn-horizontal>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
|
|
|
@ -9,4 +9,5 @@ Price in kg: Precio en kg
|
||||||
New intrastat: Nuevo intrastat
|
New intrastat: Nuevo intrastat
|
||||||
Identifier: Identificador
|
Identifier: Identificador
|
||||||
Fragile: Frágil
|
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"
|
model="model"
|
||||||
class="vn-w-xl vn-mb-xl">
|
class="vn-w-xl vn-mb-xl">
|
||||||
<vn-card>
|
<vn-card>
|
||||||
<vn-table
|
<vn-table
|
||||||
model="model"
|
model="model"
|
||||||
show-fields="$ctrl.showFields"
|
show-fields="$ctrl.showFields"
|
||||||
vn-smart-table="itemIndex">
|
vn-smart-table="itemIndex">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
<vn-th field="id" shrink>Id</vn-th>
|
<vn-th field="id" shrink>Id</vn-th>
|
||||||
<vn-th field="grouping" shrink>Grouping</vn-th>
|
<vn-th field="grouping" shrink>Grouping</vn-th>
|
||||||
<vn-th field="packing" shrink>Packing</vn-th>
|
<vn-th field="packing" shrink>Packing</vn-th>
|
||||||
<vn-th field="description">Description</vn-th>
|
<vn-th field="name">Description</vn-th>
|
||||||
<vn-th field="stems" shrink>Stems</vn-th>
|
<vn-th field="stems" shrink>Stems</vn-th>
|
||||||
<vn-th field="size" shrink>Size</vn-th>
|
<vn-th field="size" shrink>Size</vn-th>
|
||||||
<vn-th field="niche" shrink>Niche</vn-th>
|
<vn-th field="niche" shrink>Niche</vn-th>
|
||||||
<vn-th field="type" shrink>Type</vn-th>
|
<vn-th field="typeFk" shrink>Type</vn-th>
|
||||||
<vn-th field="category" shrink>Category</vn-th>
|
<vn-th field="category" shrink>Category</vn-th>
|
||||||
<vn-th field="intrastat" shrink>Intrastat</vn-th>
|
<vn-th field="intrastat" shrink>Intrastat</vn-th>
|
||||||
<vn-th field="origin" shrink>Origin</vn-th>
|
<vn-th field="origin" shrink>Origin</vn-th>
|
||||||
<vn-th field="salesperson" shrink>Buyer</vn-th>
|
<vn-th field="salesperson" shrink>Buyer</vn-th>
|
||||||
<vn-th field="density" shrink>Density</vn-th>
|
<vn-th field="density" shrink>Density</vn-th>
|
||||||
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
||||||
<vn-th field="active" shrink>Active</vn-th>
|
<vn-th field="active" shrink>Active</vn-th>
|
||||||
<vn-th></vn-th>
|
<vn-th></vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<a ng-repeat="item in model.data"
|
<a ng-repeat="item in model.data"
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ui-sref="item.card.summary({id: item.id})">
|
ui-sref="item.card.summary({id: item.id})">
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<img
|
<img
|
||||||
ng-src="{{::$root.imagePath('catalog', '50x50', item.id)}}"
|
ng-src="{{::$root.imagePath('catalog', '50x50', item.id)}}"
|
||||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
||||||
vn-click-stop
|
vn-click-stop
|
||||||
on-error-src/>
|
on-error-src/>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<span
|
<span
|
||||||
vn-click-stop="itemDescriptor.show($event, item.id)"
|
vn-click-stop="itemDescriptor.show($event, item.id)"
|
||||||
class="link">
|
class="link">
|
||||||
{{::item.id}}
|
{{::item.id}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::item.grouping | dashIfEmpty}}</vn-td>
|
<vn-td shrink>{{::item.grouping | dashIfEmpty}}</vn-td>
|
||||||
<vn-td shrink>{{::item.packing | dashIfEmpty}}</vn-td>
|
<vn-td shrink>{{::item.packing | dashIfEmpty}}</vn-td>
|
||||||
<vn-td vn-fetched-tags>
|
<vn-td vn-fetched-tags>
|
||||||
<vn-one title="{{::item.name}}">{{::item.name}}</vn-one>
|
<vn-one title="{{::item.name}}">{{::item.name}}</vn-one>
|
||||||
<vn-one ng-if="::item.subName">
|
<vn-one ng-if="::item.subName">
|
||||||
<h3 title="{{::item.subName}}">{{::item.subName}}</h3>
|
<h3 title="{{::item.subName}}">{{::item.subName}}</h3>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-fetched-tags
|
<vn-fetched-tags
|
||||||
max-length="6"
|
max-length="6"
|
||||||
item="item"
|
item="item"
|
||||||
tabindex="-1">
|
tabindex="-1">
|
||||||
</vn-fetched-tags>
|
</vn-fetched-tags>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::item.stems}}</vn-td>
|
<vn-td shrink>{{::item.stems}}</vn-td>
|
||||||
<vn-td shrink>{{::item.size}}</vn-td>
|
<vn-td shrink>{{::item.size}}</vn-td>
|
||||||
<vn-td shrink>{{::item.niche}}</vn-td>
|
<vn-td shrink>{{::item.niche}}</vn-td>
|
||||||
<vn-td shrink title="{{::item.type}}">
|
<vn-td shrink title="{{::item.typeName}}">
|
||||||
{{::item.type}}
|
{{::item.typeName}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink title="{{::item.category}}">
|
<vn-td shrink title="{{::item.category}}">
|
||||||
{{::item.category}}
|
{{::item.category}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink title="{{::item.intrastat}}">
|
<vn-td shrink title="{{::item.intrastat}}">
|
||||||
{{::item.intrastat}}
|
{{::item.intrastat}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::item.origin}}</vn-td>
|
<vn-td shrink>{{::item.origin}}</vn-td>
|
||||||
<vn-td shrink title="{{::item.userName}}">
|
<vn-td shrink title="{{::item.userName}}">
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
vn-click-stop="workerDescriptor.show($event, item.buyerFk)">
|
vn-click-stop="workerDescriptor.show($event, item.buyerFk)">
|
||||||
{{::item.userName}}
|
{{::item.userName}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::item.density}}</vn-td>
|
<vn-td shrink>{{::item.density}}</vn-td>
|
||||||
<vn-td shrink >{{::item.stemMultiplier}}</vn-td>
|
<vn-td shrink >{{::item.stemMultiplier}}</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-check
|
<vn-check
|
||||||
disabled="true"
|
disabled="true"
|
||||||
ng-model="::item.isActive">
|
ng-model="::item.isActive">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-horizontal class="buttons">
|
<vn-horizontal class="buttons">
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-click-stop="clone.show(item.id)"
|
vn-click-stop="clone.show(item.id)"
|
||||||
vn-tooltip="Clone"
|
vn-tooltip="Clone"
|
||||||
icon="icon-clone">
|
icon="icon-clone">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-click-stop="$ctrl.preview(item)"
|
vn-click-stop="$ctrl.preview(item)"
|
||||||
vn-tooltip="Preview"
|
vn-tooltip="Preview"
|
||||||
icon="preview">
|
icon="preview">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
</a>
|
</a>
|
||||||
</vn-tbody>
|
</vn-tbody>
|
||||||
</vn-table>
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
|
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
|
||||||
|
@ -127,4 +127,31 @@
|
||||||
<vn-item-summary
|
<vn-item-summary
|
||||||
item="$ctrl.itemSelected">
|
item="$ctrl.itemSelected">
|
||||||
</vn-item-summary>
|
</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) {
|
onCloneAccept(itemFk) {
|
||||||
return this.$http.post(`Items/${itemFk}/clone`)
|
return this.$http.post(`Items/${itemFk}/clone`)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
<vn-label-value label="stems"
|
<vn-label-value label="stems"
|
||||||
value="{{$ctrl.summary.item.stems}}">
|
value="{{$ctrl.summary.item.stems}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Multiplier"
|
||||||
|
value="{{$ctrl.summary.item.stemMultiplier}}">
|
||||||
|
</vn-label-value>
|
||||||
<vn-label-value label="Buyer">
|
<vn-label-value label="Buyer">
|
||||||
<span
|
<span
|
||||||
ng-click="workerDescriptor.show($event, $ctrl.summary.item.itemType.worker.userFk)"
|
ng-click="workerDescriptor.show($event, $ctrl.summary.item.itemType.worker.userFk)"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('getAverageDays', {
|
Self.remoteMethod('getAverageDays', {
|
||||||
description: 'Returns the average days duration and the two warehouses of the travel.',
|
description: 'Returns the average days duration and the two warehouses of the travel.',
|
||||||
|
@ -8,7 +9,7 @@ module.exports = Self => {
|
||||||
required: true
|
required: true
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'number',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -18,15 +19,48 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.getAverageDays = async agencyModeFk => {
|
Self.getAverageDays = async agencyModeFk => {
|
||||||
const query = `
|
const conn = Self.dataSource.connector;
|
||||||
SELECT t.id, t.warehouseInFk, t.warehouseOutFk,
|
let stmts = [];
|
||||||
(SELECT ROUND(AVG(DATEDIFF(t.landed, t.shipped )))
|
|
||||||
FROM travel t
|
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.travel');
|
||||||
WHERE t.agencyFk = ? LIMIT 50) AS dayDuration
|
|
||||||
FROM travel t
|
stmt = new ParameterizedSQL(`
|
||||||
WHERE t.agencyFk = ? ORDER BY t.id DESC LIMIT 1;`;
|
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;
|
return avgDays;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,9 @@ class Controller extends Section {
|
||||||
agencyModeFk: this.travel.agencyModeFk
|
agencyModeFk: this.travel.agencyModeFk
|
||||||
};
|
};
|
||||||
this.$http.get(query, {params}).then(res => {
|
this.$http.get(query, {params}).then(res => {
|
||||||
|
if (!res.data)
|
||||||
|
return;
|
||||||
|
|
||||||
const landed = new Date(value);
|
const landed = new Date(value);
|
||||||
const futureDate = landed.getDate() + res.data.dayDuration;
|
const futureDate = landed.getDate() + res.data.dayDuration;
|
||||||
landed.setDate(futureDate);
|
landed.setDate(futureDate);
|
||||||
|
|
|
@ -51,14 +51,29 @@ describe('Travel Component vnTravelCreate', () => {
|
||||||
expect(controller.travel.warehouseOutFk).toBeUndefined();
|
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.`, () => {
|
it(`should fill the fields when it's selected a date and agency.`, () => {
|
||||||
controller.travel = {agencyModeFk: 1};
|
controller.travel = {agencyModeFk: 1};
|
||||||
const tomorrow = new Date();
|
const tomorrow = new Date();
|
||||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
tomorrow.setDate(tomorrow.getDate() + 9);
|
||||||
const expectedResponse = {
|
const expectedResponse = {
|
||||||
dayDuration: 2,
|
id: 8,
|
||||||
warehouseInFk: 1,
|
dayDuration: 9,
|
||||||
warehouseOutFk: 2
|
warehouseInFk: 5,
|
||||||
|
warehouseOutFk: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
|
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
|
||||||
|
|
|
@ -42,6 +42,11 @@
|
||||||
"model": "Account",
|
"model": "Account",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "userFk"
|
||||||
},
|
},
|
||||||
|
"boss": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "bossFk"
|
||||||
|
},
|
||||||
"client": {
|
"client": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Client",
|
"model": "Client",
|
||||||
|
|
|
@ -29,6 +29,15 @@
|
||||||
ng-model="$ctrl.worker.phone"
|
ng-model="$ctrl.worker.phone"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</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-horizontal>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -7,6 +7,7 @@ Extension: Extensión
|
||||||
Fiscal identifier: NIF
|
Fiscal identifier: NIF
|
||||||
Go to client: Ir al cliente
|
Go to client: Ir al cliente
|
||||||
Last name: Apellidos
|
Last name: Apellidos
|
||||||
|
Boss: Jefe
|
||||||
Log: Historial
|
Log: Historial
|
||||||
Private Branch Exchange: Centralita
|
Private Branch Exchange: Centralita
|
||||||
Role: Rol
|
Role: Rol
|
||||||
|
|
|
@ -30,6 +30,14 @@
|
||||||
<vn-label-value label="Department"
|
<vn-label-value label="Department"
|
||||||
value="{{worker.department.department.name}}">
|
value="{{worker.department.department.name}}">
|
||||||
</vn-label-value>
|
</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"
|
<vn-label-value label="Phone"
|
||||||
value="{{worker.phone}}">
|
value="{{worker.phone}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
@ -50,4 +58,7 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-horizontal>
|
</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;
|
this.$.worker = null;
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
let query = `Workers/${value.id}`;
|
const query = `Workers/${value.id}`;
|
||||||
let filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
relation: 'user',
|
relation: 'user',
|
||||||
|
@ -31,13 +31,20 @@ class Controller extends Summary {
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {fields: ['fi']}
|
scope: {fields: ['fi']}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
|
relation: 'boss',
|
||||||
|
scope: {fields: ['id', 'nickname']}
|
||||||
|
},
|
||||||
|
{
|
||||||
relation: 'sip',
|
relation: 'sip',
|
||||||
scope: {fields: ['extension']}
|
scope: {fields: ['extension']}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
relation: 'department',
|
relation: 'department',
|
||||||
scope: {
|
scope: {
|
||||||
include: {
|
include: {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,10 +12,12 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bmp-js": "^0.1.0",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
"i18n": "^0.8.4",
|
"i18n": "^0.8.4",
|
||||||
|
"image-type": "^4.1.0",
|
||||||
"imap": "^0.8.19",
|
"imap": "^0.8.19",
|
||||||
"ldapjs": "^2.2.0",
|
"ldapjs": "^2.2.0",
|
||||||
"loopback": "^3.26.0",
|
"loopback": "^3.26.0",
|
||||||
|
@ -30,6 +32,7 @@
|
||||||
"node-ssh": "^11.0.0",
|
"node-ssh": "^11.0.0",
|
||||||
"object-diff": "0.0.4",
|
"object-diff": "0.0.4",
|
||||||
"object.pick": "^1.3.0",
|
"object.pick": "^1.3.0",
|
||||||
|
"read-chunk": "^3.2.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
"require-yaml": "0.0.1",
|
"require-yaml": "0.0.1",
|
||||||
|
|
Loading…
Reference in New Issue