2804 Fixed problem with link of Sales person on ticket descriptor #558
|
@ -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);
|
||||||
|
|
|
@ -174,4 +174,9 @@ vn-table {
|
||||||
.vn-check {
|
.vn-check {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
.empty-rows {
|
||||||
|
color: $color-font-secondary;
|
||||||
|
font-size: 1.375rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -26,15 +26,25 @@ module.exports = Self => {
|
||||||
Self.lastActiveTickets = async(id, ticketId) => {
|
Self.lastActiveTickets = async(id, ticketId) => {
|
||||||
const ticket = await Self.app.models.Ticket.findById(ticketId);
|
const ticket = await Self.app.models.Ticket.findById(ticketId);
|
||||||
const query = `
|
const query = `
|
||||||
SELECT t.id, t.shipped, a.name AS agencyName, w.name AS warehouseName, ad.city AS address
|
SELECT
|
||||||
FROM vn.ticket t
|
t.id,
|
||||||
JOIN vn.ticketState ts ON t.id = ts.ticketFk
|
t.shipped,
|
||||||
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
|
a.name AS agencyName,
|
||||||
JOIN vn.warehouse w ON t.warehouseFk = w.id
|
w.name AS warehouseName,
|
||||||
JOIN vn.address ad ON t.addressFk = ad.id
|
ad.nickname AS nickname,
|
||||||
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
|
ad.city AS city,
|
||||||
AND t.id <> ? AND t.warehouseFk = ?
|
ad.postalCode AS postalCode,
|
||||||
ORDER BY t.shipped
|
ad.street AS street,
|
||||||
|
pr.name AS name
|
||||||
|
FROM ticket t
|
||||||
|
JOIN vn.ticketState ts ON t.id = ts.ticketFk
|
||||||
|
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
|
||||||
|
JOIN vn.warehouse w ON t.warehouseFk = w.id
|
||||||
|
JOIN vn.address ad ON t.addressFk = ad.id
|
||||||
|
JOIN vn.province pr ON ad.provinceFk = pr.id
|
||||||
|
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
|
||||||
|
AND t.id <> ? AND t.warehouseFk = ?
|
||||||
|
ORDER BY t.shipped
|
||||||
LIMIT 10`;
|
LIMIT 10`;
|
||||||
|
|
||||||
return Self.rawSql(query, [id, ticketId, ticket.warehouseFk]);
|
return Self.rawSql(query, [id, ticketId, ticket.warehouseFk]);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('Client last active tickets', () => {
|
||||||
|
it('should receive an array of last active tickets of Bruce Wayne', async() => {
|
||||||
|
const ticketId = 22;
|
||||||
|
const clientId = 109;
|
||||||
|
const warehouseId = 5;
|
||||||
|
const result = await app.models.Client.lastActiveTickets(clientId, ticketId, warehouseId);
|
||||||
|
|
||||||
|
const length = result.length;
|
||||||
|
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
|
||||||
|
|
||||||
|
const properties = Object.keys(anyResult);
|
||||||
|
|
||||||
|
expect(properties.length).toEqual(9);
|
||||||
|
expect(result.length).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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
|
||||||
|
|
|
@ -10,3 +10,4 @@ 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>
|
||||||
|
@ -128,3 +128,30 @@
|
||||||
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 => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="search-panel">
|
<div class="search-panel">
|
||||||
<form class="vn-pa-lg" ng-submit="$ctrl.onSearch()">
|
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||||
<vn-horizontal>
|
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="General search"
|
label="General search"
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal class="vn-px-lg">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Ticket id"
|
label="Ticket id"
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<tpl-item>{{nickname}}</tpl-item>
|
<tpl-item>{{nickname}}</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal class="vn-px-lg">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Client id"
|
label="Client id"
|
||||||
|
@ -38,19 +38,37 @@
|
||||||
url="Warehouses">
|
url="Warehouses">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<section class="vn-px-md">
|
||||||
|
<vn-horizontal class="manifold-panel vn-pa-md">
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
vn-one
|
vn-one
|
||||||
label="From"
|
label="From"
|
||||||
ng-model="filter.from">
|
ng-model="filter.from"
|
||||||
|
on-change="$ctrl.from = value">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
vn-one
|
vn-one
|
||||||
label="To"
|
label="To"
|
||||||
ng-model="filter.to">
|
ng-model="filter.to"
|
||||||
|
on-change="$ctrl.to = value">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
|
<vn-none class="or vn-px-md" translate>Or</vn-none>
|
||||||
|
<vn-input-number
|
||||||
|
vn-one
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
label="Days onward"
|
||||||
|
ng-model="filter.scopeDays"
|
||||||
|
on-change="$ctrl.scopeDays = value"
|
||||||
|
display-controls="true">
|
||||||
|
</vn-input-number>
|
||||||
|
<vn-icon color-marginal
|
||||||
|
icon="info"
|
||||||
|
vn-tooltip="Cannot choose a range of dates and days onward at the same time">
|
||||||
|
</vn-icon>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
</section>
|
||||||
|
<vn-horizontal class="vn-px-lg">
|
||||||
<vn-check vn-one
|
<vn-check vn-one
|
||||||
triple-state="true"
|
triple-state="true"
|
||||||
label="For me"
|
label="For me"
|
||||||
|
@ -65,7 +83,7 @@
|
||||||
<tpl-item>{{name}}</tpl-item>
|
<tpl-item>{{name}}</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-mt-lg">
|
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
|
||||||
<vn-submit label="Search"></vn-submit>
|
<vn-submit label="Search"></vn-submit>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -11,6 +11,35 @@ class Controller extends SearchPanel {
|
||||||
{code: 'denied', name: this.$t('Denied')}
|
{code: 'denied', name: this.$t('Denied')}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get from() {
|
||||||
|
return this._from;
|
||||||
|
}
|
||||||
|
|
||||||
|
set from(value) {
|
||||||
|
this._from = value;
|
||||||
|
this.filter.scopeDays = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get to() {
|
||||||
|
return this._to;
|
||||||
|
}
|
||||||
|
|
||||||
|
set to(value) {
|
||||||
|
this._to = value;
|
||||||
|
this.filter.scopeDays = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get scopeDays() {
|
||||||
|
return this._scopeDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
set scopeDays(value) {
|
||||||
|
this._scopeDays = value;
|
||||||
|
|
||||||
|
this.filter.from = null;
|
||||||
|
this.filter.to = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnRequestSearchPanel', {
|
ngModule.vnComponent('vnRequestSearchPanel', {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import './index';
|
||||||
|
|
||||||
|
describe(' Component vnRequestSearchPanel', () => {
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
beforeEach(ngModule('item'));
|
||||||
|
|
||||||
|
beforeEach(inject($componentController => {
|
||||||
|
controller = $componentController('vnRequestSearchPanel', {$element: null});
|
||||||
|
controller.$t = () => {};
|
||||||
|
controller.filter = {};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('from() setter', () => {
|
||||||
|
it('should clear the scope days when setting the from property', () => {
|
||||||
|
controller.filter.scopeDays = 1;
|
||||||
|
|
||||||
|
controller.from = new Date();
|
||||||
|
|
||||||
|
expect(controller.filter.scopeDays).toBeNull();
|
||||||
|
expect(controller.from).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('to() setter', () => {
|
||||||
|
it('should clear the scope days when setting the to property', () => {
|
||||||
|
controller.filter.scopeDays = 1;
|
||||||
|
|
||||||
|
controller.to = new Date();
|
||||||
|
|
||||||
|
expect(controller.filter.scopeDays).toBeNull();
|
||||||
|
expect(controller.to).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scopeDays() setter', () => {
|
||||||
|
it('should clear the date range when setting the scopeDays property', () => {
|
||||||
|
controller.filter.from = new Date();
|
||||||
|
controller.filter.to = new Date();
|
||||||
|
|
||||||
|
controller.scopeDays = 1;
|
||||||
|
|
||||||
|
expect(controller.filter.from).toBeNull();
|
||||||
|
expect(controller.filter.to).toBeNull();
|
||||||
|
expect(controller.scopeDays).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -10,9 +10,10 @@
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
panel="vn-request-search-panel"
|
panel="vn-request-search-panel"
|
||||||
suggested-filter="$ctrl.filterParams"
|
|
||||||
info="Search request by id or alias"
|
info="Search request by id or alias"
|
||||||
|
suggested-filter="$ctrl.filterParams"
|
||||||
filter="$ctrl.filterParams"
|
filter="$ctrl.filterParams"
|
||||||
|
fetch-params="$ctrl.fetchParams($params)"
|
||||||
model="model"
|
model="model"
|
||||||
auto-state="false">
|
auto-state="false">
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
|
|
|
@ -15,7 +15,6 @@ export default class Controller extends Section {
|
||||||
nextWeek.setDate(nextWeek.getDate() + 7);
|
nextWeek.setDate(nextWeek.getDate() + 7);
|
||||||
|
|
||||||
this.filterParams = {
|
this.filterParams = {
|
||||||
mine: true,
|
|
||||||
from: today,
|
from: today,
|
||||||
to: nextWeek,
|
to: nextWeek,
|
||||||
state: 'pending'
|
state: 'pending'
|
||||||
|
@ -23,6 +22,24 @@ export default class Controller extends Section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchParams($params) {
|
||||||
|
if (!Object.entries($params).length)
|
||||||
|
$params.scopeDays = 1;
|
||||||
|
|
||||||
|
if (typeof $params.scopeDays === 'number') {
|
||||||
|
const from = new Date();
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const to = new Date(from.getTime());
|
||||||
|
to.setDate(to.getDate() + $params.scopeDays);
|
||||||
|
to.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
|
Object.assign($params, {from, to});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
getState(isOk) {
|
getState(isOk) {
|
||||||
if (isOk === null)
|
if (isOk === null)
|
||||||
return 'Pending';
|
return 'Pending';
|
||||||
|
|
|
@ -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)"
|
||||||
|
|
|
@ -326,31 +326,49 @@
|
||||||
icon="info">
|
icon="info">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-table class="destinationTable">
|
<table class="destinationTable vn-table">
|
||||||
<vn-thead>
|
<thead>
|
||||||
<vn-tr>
|
<tr>
|
||||||
<vn-th number>Id</vn-th>
|
<th translate shrink>Id</th>
|
||||||
<vn-th number>Shipped</vn-th>
|
<th translate>Shipped</th>
|
||||||
<vn-th number>Agency</vn-th>
|
<th translate shrink>Agency</th>
|
||||||
<vn-th number>Warehouse</vn-th>
|
<th translate expand>Address</th>
|
||||||
<vn-th number>Address</vn-th>
|
</tr>
|
||||||
</vn-tr>
|
</thead>
|
||||||
</vn-thead>
|
<tbody>
|
||||||
<vn-tbody>
|
<tr
|
||||||
<vn-data-viewer data="$ctrl.transfer.lastActiveTickets">
|
|
||||||
</vn-data-viewer>
|
|
||||||
<vn-tr
|
|
||||||
class="clickable"
|
class="clickable"
|
||||||
ng-repeat="ticket in $ctrl.transfer.lastActiveTickets track by ticket.id"
|
ng-repeat="ticket in $ctrl.transfer.lastActiveTickets track by ticket.id"
|
||||||
ng-click="$ctrl.transferSales(ticket.id)">
|
ng-click="$ctrl.transferSales(ticket.id)">
|
||||||
<vn-td number>{{::ticket.id}}</vn-td>
|
<td shrink>{{::ticket.id}}</td>
|
||||||
<vn-td number>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</vn-td>
|
<td>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</td>
|
||||||
<vn-td number>{{::ticket.agencyName}}</vn-td>
|
<td shrink>{{::ticket.agencyName}}</td>
|
||||||
<vn-td number>{{::ticket.warehouseName}}</vn-td>
|
<td expand>{{::ticket.address}}
|
||||||
<vn-td number>{{::ticket.address}}</vn-td>
|
<span vn-tooltip="
|
||||||
</vn-tr>
|
{{::ticket.nickname}}
|
||||||
</vn-tbody>
|
{{::ticket.name}}
|
||||||
</vn-table>
|
{{::ticket.street}}
|
||||||
|
{{::ticket.postalCode}}
|
||||||
|
{{::ticket.city}}">
|
||||||
|
{{::ticket.nickname}}
|
||||||
|
{{::ticket.name}}
|
||||||
|
{{::ticket.street}}
|
||||||
|
{{::ticket.postalCode}}
|
||||||
|
{{::ticket.city}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
ng-if="!$ctrl.transfer.lastActiveTickets.length"
|
||||||
|
class="empty-rows"
|
||||||
|
colspan="4"
|
||||||
|
translate>
|
||||||
|
No results
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<form name="form">
|
<form name="form">
|
||||||
<vn-horizontal class="vn-py-md">
|
<vn-horizontal class="vn-py-md">
|
||||||
<vn-input-number vn-one
|
<vn-input-number vn-one
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -51,3 +59,6 @@
|
||||||
</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