2569 - Section changes
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Joan Sanchez 2020-11-20 15:05:57 +01:00
parent f4e9fc83ec
commit 7693a1c449
25 changed files with 635 additions and 173 deletions

View File

@ -20,6 +20,9 @@
"Container": { "Container": {
"dataSource": "storage" "dataSource": "storage"
}, },
"Continent": {
"dataSource": "vn"
},
"Collection": { "Collection": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,27 @@
{
"name": "Continent",
"base": "VnModel",
"options": {
"mysql": {
"table": "continent"
}
},
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string"
},
"code": {
"id": true,
"type": "string"
}
},
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -0,0 +1,20 @@
CREATE TABLE `vn`.continent
(
id TINYINT(4) AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
code VARCHAR(2) NOT NULL COLLATE utf8_general_ci,
CONSTRAINT continent_pk
PRIMARY KEY (id)
)
COMMENT 'World continents';
CREATE UNIQUE INDEX continent_name_uindex
ON `vn`.continent (name);
INSERT IGNORE INTO `vn`.continent (`name`, `code`)
VALUES
('Asia', 'AS'),
('América', 'AM'),
('África', 'AF'),
('Europa', 'EU'),
('Oceanía', 'OC');

View File

@ -0,0 +1,13 @@
ALTER TABLE `vn`.`country`
ADD COLUMN `continentFk` TINYINT(4) NULL AFTER `ibanLength`,
ADD INDEX `continent_id_fk_idx` (`continentFk` ASC);
ALTER TABLE `vn`.`country`
ADD CONSTRAINT `continent_id_fk`
FOREIGN KEY (`continentFk`)
REFERENCES `vn`.`continent` (`id`)
ON DELETE NO ACTION
ON UPDATE CASCADE;
UPDATE `vn`.`country` SET `continentFk` = '2' WHERE (`id` = '11');
UPDATE `vn`.`country` SET `continentFk` = '2' WHERE (`id` = '13');

View File

@ -3,3 +3,4 @@ host = localhost
port = 3306 port = 3306
user = root user = root
password = root password = root
default-character-set=utf8

View File

@ -92,17 +92,17 @@ INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossF
(109, 'HLK', 'Bruce' , 'Banner', 109, 19, 432978109), (109, 'HLK', 'Bruce' , 'Banner', 109, 19, 432978109),
(110, 'JJJ', 'Jessica' , 'Jones' , 110, 19, 432978110); (110, 'JJJ', 'Jessica' , 'Jones' , 110, 19, 432978110);
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`) INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`)
VALUES VALUES
(1, 'España', 1, 'ES', 1, 24), (1, 'España', 1, 'ES', 1, 24, 4),
(2, 'Italia', 1, 'IT', 1, 27), (2, 'Italia', 1, 'IT', 1, 27, 4),
(3, 'Alemania', 1, 'DE', 1, 22), (3, 'Alemania', 1, 'DE', 1, 22, 4),
(4, 'Rumania', 1, 'RO', 1, 24), (4, 'Rumania', 1, 'RO', 1, 24, 4),
(5, 'Holanda', 1, 'NL', 1, 18), (5, 'Holanda', 1, 'NL', 1, 18, 4),
(8, 'Portugal', 1, 'PT', 1, 27), (8, 'Portugal', 1, 'PT', 1, 27, 4),
(13,'Ecuador', 0, 'EC', 1, 24), (13,'Ecuador', 0, 'EC', 1, 24, 2),
(19,'Francia', 1, 'FR', 1, 27), (19,'Francia', 1, 'FR', 1, 27, 4),
(30,'Canarias', 1, 'IC', 1, 24); (30,'Canarias', 1, 'IC', 1, 24, 4);
INSERT INTO `hedera`.`language` (`code`, `name`, `orgName`, `isActive`) INSERT INTO `hedera`.`language` (`code`, `name`, `orgName`, `isActive`)
VALUES VALUES
@ -118,13 +118,13 @@ INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
(1, 'Main Warehouse'), (1, 'Main Warehouse'),
(2, 'Silla'); (2, 'Silla');
INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`, `hasComission`, `aliasFk`) INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`, `hasComission`, `aliasFk`, `countryFk`)
VALUES VALUES
(1, 'Warehouse One', 1, 1, 1, 1, 1, 1, 1, 2), (1, 'Warehouse One', 1, 1, 1, 1, 1, 1, 1, 2, 1),
(2, 'Warehouse Two', 1, 1, 1, 1, 0, 0, 1, 2), (2, 'Warehouse Two', 1, 1, 1, 1, 0, 0, 1, 2, 13),
(3, 'Warehouse Three', 1, 1, 1, 1, 0, 0, 0, 2), (3, 'Warehouse Three', 1, 1, 1, 1, 0, 0, 0, 2, 1),
(4, 'Warehouse Four', 1, 1, 1, 1, 0, 0, 0, 2), (4, 'Warehouse Four', 1, 1, 1, 1, 0, 0, 0, 2, 1),
(5, 'Warehouse Five', 1, 1, 1, 1, 0, 0, 0, 2); (5, 'Warehouse Five', 1, 1, 1, 1, 0, 0, 0, 2, 1);
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`, `pickingPlacement`, `path`) INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`, `pickingPlacement`, `path`)
VALUES VALUES
@ -1250,11 +1250,11 @@ INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseO
(1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1, 1), (1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1, 1),
(2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2, 2), (2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2, 2),
(3, CURDATE(), CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1, 1), (3, CURDATE(), CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1, 1),
(4, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 50.00, 500, 'fourth travel', 0, 2), (4, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 3, 1, 50.00, 500, 'fourth travel', 0, 2),
(5, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 3, 2, 1, 50.00, 500, 'fifth travel', 1, 1), (5, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 3, 3, 1, 50.00, 500, 'fifth travel', 1, 1),
(6, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 2, 1, 50.00, 500, 'sixth travel', 1, 2), (6, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 4, 1, 50.00, 500, 'sixth travel', 1, 2),
(7, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'seventh travel', 2, 1), (7, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 4, 1, 50.00, 500, 'seventh travel', 2, 1),
(8, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'eight travel', 1, 2); (8, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 1, 1, 50.00, 500, 'eight travel', 1, 2);
INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `ref`,`isInventory`, `isRaid`, `notes`, `evaNotes`) INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `ref`,`isInventory`, `isRaid`, `notes`, `evaNotes`)
VALUES VALUES

View File

@ -1 +1,3 @@
<div></div> <div translate>
<ng-transclude></ng-transclude>
</div>

View File

@ -1,19 +1,10 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
export default class Th extends Component {
constructor($element, $, $transclude) {
super($element, $);
export default class Th {
constructor($element) {
this._order = 'ASC'; this._order = 'ASC';
this.column = $element[0]; this.column = $element[0];
$element.on('click', () => this.onToggleOrder()); $element.on('click', () => this.onToggleOrder());
$transclude($clone => {
const text = this.$t($clone.text()); // Not updating translations
$element.append(text ? text : $clone);
});
} }
/** /**
@ -81,9 +72,10 @@ export default class Th extends Component {
} }
} }
Th.$inject = ['$element', '$scope', '$transclude']; Th.$inject = ['$element'];
ngModule.vnComponent('vnTh', { ngModule.vnComponent('vnTh', {
template: require('./index.html'),
transclude: true, transclude: true,
controller: Th, controller: Th,
bindings: { bindings: {

View File

@ -6,18 +6,18 @@ vn-th {
} }
vn-th[field] { vn-th[field] {
&.active:after { &.active > :after {
color: $color-font; color: $color-font;
opacity: 1; opacity: 1;
} }
&.desc:after { &.desc > :after {
content: 'arrow_drop_down'; content: 'arrow_drop_down';
} }
&.asc:after { &.asc > :after {
content: 'arrow_drop_up'; content: 'arrow_drop_up';
} }
&:after { & > :after {
font-family: 'Material Icons'; font-family: 'Material Icons';
content: 'arrow_drop_down'; content: 'arrow_drop_down';
position: absolute; position: absolute;
@ -27,7 +27,7 @@ vn-th[field] {
opacity: 0 opacity: 0
} }
&:hover:after { &:hover > :after {
opacity: 1; opacity: 1;
} }
} }

View File

@ -17,7 +17,7 @@
ui-sref="{{::$ctrl.summaryState}}({id: $ctrl.descriptor.id})"> ui-sref="{{::$ctrl.summaryState}}({id: $ctrl.descriptor.id})">
<vn-icon icon="desktop_windows"></vn-icon> <vn-icon icon="desktop_windows"></vn-icon>
</a> </a>
<vn-icon-button ng-if="$ctrl.$transclude.isSlotFilled('menu')" <vn-icon-button ng-if="!$ctrl.$transclude.isSlotFilled('dotMenu')"
ng-class="::{invisible: !$ctrl.$transclude.isSlotFilled('menu')}" ng-class="::{invisible: !$ctrl.$transclude.isSlotFilled('menu')}"
icon="more_vert" icon="more_vert"
vn-popover="menu"> vn-popover="menu">

View File

@ -84,6 +84,32 @@ class VnMySQL extends MySQL {
return wrappedConnector.buildWhere(null, where); return wrappedConnector.buildWhere(null, where);
} }
/**
* Constructs SQL GROUP BY clause from Loopback filter.
*
* @param {Object} group The group by definition
* @return {String} Built SQL group by
*/
makeGroupBy(group) {
if (!group)
return '';
if (typeof group === 'string')
group = [group];
let clauses = [];
for (let clause of group) {
let sqlGroup = '';
let t = clause.split(/[\s,]+/);
sqlGroup += this.escapeName(t[0]);
clauses.push(sqlGroup);
}
return `GROUP BY ${clauses.join(', ')}`;
}
/** /**
* Constructs SQL order clause from Loopback filter. * Constructs SQL order clause from Loopback filter.
* *

View File

@ -5,10 +5,8 @@ describe('item getBalance()', () => {
let params = {where: {itemFk: 1, warehouseFk: 2}}; let params = {where: {itemFk: 1, warehouseFk: 2}};
let result = await app.models.Item.getBalance(params); let result = await app.models.Item.getBalance(params);
expect(result.length).toBe(4); expect(result.length).toBe(2);
expect(result[0].balance).toBe(-100); expect(result[0].balance).toBe(-100);
expect(result[1].balance).toBe(-110); expect(result[1].balance).toBe(-200);
expect(result[2].balance).toBe(-110);
expect(result[3].balance).toBe(-210);
}); });
}); });

View File

@ -67,6 +67,10 @@ module.exports = Self => {
arg: 'ref', arg: 'ref',
type: 'string', type: 'string',
description: 'The reference' description: 'The reference'
}, {
arg: 'continent',
type: 'string',
description: 'The continent code'
} }
], ],
returns: { returns: {
@ -97,6 +101,8 @@ module.exports = Self => {
return {'t.landed': {gte: value}}; return {'t.landed': {gte: value}};
case 'landedTo': case 'landedTo':
return {'t.landed': {lte: value}}; return {'t.landed': {lte: value}};
case 'continent':
return {'cnt.code': value};
case 'id': case 'id':
case 'agencyFk': case 'agencyFk':
case 'warehouseOutFk': case 'warehouseOutFk':
@ -107,17 +113,21 @@ module.exports = Self => {
} }
}); });
filter = mergeFilters(ctx.args.filter, {where}); filter = mergeFilters(filter, {where});
let stmts = []; let stmts = [];
let stmt; let stmt;
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.travel');
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`SELECT `CREATE TEMPORARY TABLE tmp.travel
tr.id, (INDEX (id))
tr.ref, ENGINE = MEMORY
tr.shipped, SELECT
tr.landed, t.id,
tr.kg, t.ref,
t.shipped,
t.landed,
t.kg,
am.id AS agencyModeFk, am.id AS agencyModeFk,
am.name AS agencyModeName, am.name AS agencyModeName,
wo.id AS warehouseOutFk, wo.id AS warehouseOutFk,
@ -126,102 +136,78 @@ module.exports = Self => {
w.name AS warehouseInName, w.name AS warehouseInName,
SUM(b.stickers) AS stickers, SUM(b.stickers) AS stickers,
s.id AS supplierFk, s.id AS supplierFk,
s.nickname AS supplierNickname s.nickname AS cargoSupplierNickname,
FROM travel tr CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
LEFT JOIN supplier s ON s.id = tr.cargoSupplierFk CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
LEFT JOIN entry e ON e.travelFk = tr.id FROM travel t
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
LEFT JOIN entry e ON e.travelFk = t.id
LEFT JOIN buy b ON b.entryFk = e.id LEFT JOIN buy b ON b.entryFk = e.id
LEFT JOIN packaging p ON p.id = b.packageFk LEFT JOIN packaging pkg ON pkg.id = b.packageFk
LEFT JOIN item i ON i.id = b.itemFk LEFT JOIN item i ON i.id = b.itemFk
LEFT JOIN itemType it ON it.id = i.typeFk LEFT JOIN itemType it ON it.id = i.typeFk
JOIN warehouse w ON w.id = tr.warehouseInFk JOIN warehouse w ON w.id = t.warehouseInFk
JOIN warehouse wo ON wo.id = tr.warehouseOutFk JOIN warehouse wo ON wo.id = t.warehouseOutFk
JOIN agencyMode am ON am.id = tr.agencyFk` JOIN country c ON c.id = wo.countryFk
LEFT JOIN continent cnt ON cnt.id = c.continentFk
JOIN agencyMode am ON am.id = t.agencyFk`
); );
/* cast(sum(a.density * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as loadedKg, stmt.merge(conn.makeWhere(filter.where));
cast(sum(167.5 * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as volumeKg, stmt.merge(conn.makeGroupBy('t.id'));
pc.Alias as Carguera */ stmt.merge(conn.makeLimit(filter));
stmts.push(stmt);
// WHERE tr.landing >= @vDateFrom AND (wo.name="Colombia" OR wo.name="Ecuador") const travelsIndex = stmts.push('SELECT * FROM tmp.travel') - 1;
stmt.merge('GROUP BY tr.id'); stmt = new ParameterizedSQL(
// stmt.merge(conn.makeSuffix(filter)); `SELECT
const itemsIndex = stmts.push(stmt) - 1; e.id,
e.travelFk,
e.ref,
e.loadPriority,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
e.evaNotes,
e.notes,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedkg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM entry e
JOIN tmp.travel tr ON tr.id = e.travelFk
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk`
);
stmt.merge(conn.makeGroupBy('e.id'));
stmt.merge(conn.makeOrderBy(filter.order));
const entriesIndex = stmts.push(stmt) - 1;
stmts.push(`DROP TEMPORARY TABLE tmp.travel`);
const sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql); const result = await conn.executeStmt(sql);
return itemsIndex === 0 ? result : result[itemsIndex];
/* const travels = result[travelsIndex];
SET @vDateFrom:= TIMESTAMPADD(WEEK,-12,CURDATE()); const entries = result[entriesIndex];
SELECT const travelsMap = new Map();
1 as IsTravel, for (let travel of travels)
tr.id as travel, travelsMap.set(travel.id, travel);
NULL as Entrada,
ag.Agencia,
tr.ref,
tr.shipment,
wo.name as OrigenCajas,
tr.landing,
w.name as Destino,
sum(c.Etiquetas) as Etiquetas,
NULL as Notas_Eva,
kg,
cast(sum(a.density * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
cast(sum(167.5 * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as volumeKg,
NULL as loadPriority,
NULL as Notas,
pc.Alias as Carguera
FROM travel tr
LEFT JOIN Proveedores pc ON pc.Id_Proveedor = tr.cargoSupplierFk
LEFT JOIN Entradas e ON e.travel_id = tr.id
LEFT JOIN Compres c ON c.Id_Entrada = e.Id_Entrada
LEFT JOIN Cubos cb ON cb.Id_Cubo = c.Id_Cubo
LEFT JOIN Articles a ON a.Id_Article = c.Id_Article
LEFT JOIN Tipos tp ON tp.tipo_id = a.tipo_id
JOIN warehouse w ON w.id = tr.warehouse_id
JOIN warehouse wo ON wo.id = tr.warehouse_id_out
JOIN Agencias ag ON ag.Id_Agencia = tr.agency_id
WHERE tr.landing >= @vDateFrom AND (wo.name="Colombia" OR wo.name="Ecuador")
GROUP BY tr.id
UNION ALL for (let entry of entries) {
const travel = travelsMap.get(entry.travelFk);
SELECT if (travel) {
0 as IsTravel, if (!travel.entries) travel.entries = [];
e.travel_id as travel,
e.Id_Entrada,
p.Proveedor,
e.Referencia,
tr.shipment,
wo.name as OrigenCajas,
tr.landing,
w.name as Destino,
sum(Etiquetas) as Etiquetas,
e.Notas_Eva,
NULL as kg,
cast(sum(a.density * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as loadedkg,
cast(sum(167.5 * c.Etiquetas * IF(cb.Volumen, cb.Volumen, cb.X * cb.Y * cb.Z) / 1000000 ) as DECIMAL(10,0)) as volumeKg,
loadPriority,
e.Notas,
pc.Alias as carguera
FROM Entradas e travel.entries.push(entry);
JOIN Compres c ON c.Id_Entrada = e.Id_Entrada }
JOIN Cubos cb ON cb.Id_Cubo = c.Id_Cubo }
JOIN Articles a ON a.Id_Article = c.Id_Article
JOIN Tipos tp ON tp.tipo_id = a.tipo_id return travels;
JOIN Proveedores p ON p.Id_Proveedor = e.Id_Proveedor
JOIN travel tr ON tr.id = e.travel_id
LEFT JOIN Proveedores pc ON pc.Id_Proveedor = tr.cargoSupplierFk
JOIN warehouse w ON w.id = tr.warehouse_id
JOIN warehouse wo ON wo.id = tr.warehouse_id_out
WHERE tr.landing >= @vDateFrom AND (wo.name="Colombia" OR wo.name="Ecuador")
GROUP BY e.Id_Entrada
) sub
ORDER BY landing ASC, shipment ASC,travel, IsTravel DESC, (loadPriority > 0) DESC,loadPriority, Agencia, Notas_Eva ; */
}; };
}; };

View File

@ -0,0 +1,66 @@
const app = require('vn-loopback/server/server');
describe('Travel extraCommunityFilter()', () => {
const filter = {
order: 'landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, evaNotes',
};
it('should return the travel matching "search"', async() => {
const ctx = {
args: {
search: 2
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
const firstRow = result[0];
const entries = firstRow.entries;
expect(result.length).toEqual(1);
expect(firstRow.id).toEqual(2);
expect(entries.length).toEqual(2);
});
it('should return the travel matching "search" by ref', async() => {
const ctx = {
args: {
search: 'third'
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
const firstRow = result[0];
expect(result.length).toEqual(1);
expect(firstRow.id).toEqual(3);
});
it('should return the travel matching "warehouse out"', async() => {
const ctx = {
args: {
warehouseOutFk: 2
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(3);
});
it('should return the routes matching "landed from" and "landed to"', async() => {
const from = new Date();
const to = new Date();
from.setHours(0, 0, 0, 0);
to.setHours(23, 59, 59, 999);
to.setDate(to.getDate() + 14);
const ctx = {
args: {
landedFrom: from,
landedTo: to
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(1);
});
});

View File

@ -1,6 +1,6 @@
Reference: Referencia Reference: Referencia
Wh. In: Almacén entrada Wh. In: Alm. entrada
Wh. Out: Almacén salida Wh. Out: Alm. salida
Shipped: F. envío Shipped: F. envío
Landed: F. entrega Landed: F. entrega
Total entries: Entradas totales Total entries: Entradas totales

View File

@ -0,0 +1,91 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search travels by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Reference"
ng-model="filter.ref">
</vn-textfield>
<vn-textfield
vn-one
label="Total entries"
ng-model="filter.totalEntries">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Travel id"
ng-model="filter.id">
</vn-textfield>
<vn-autocomplete vn-one
label="Agency"
ng-model="filter.agencyFk"
url="AgencyModes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Shipped from"
ng-model="filter.shippedFrom">
</vn-date-picker>
<vn-date-picker
vn-one
label="Shipped to"
ng-model="filter.shippedTo">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Landed from"
ng-model="filter.landedFrom">
</vn-date-picker>
<vn-date-picker
vn-one
label="Landed to"
ng-model="filter.landedTo">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Warehouse In"
ng-model="filter.warehouseInFk"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse Out"
ng-model="filter.warehouseOutFk"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Continent Out"
ng-model="filter.continent"
url="Continents"
show-field="name"
value-field="code">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.vnComponent('vnExtraCommunitySearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -1,49 +1,111 @@
<vn-crud-model auto-load="true" <vn-crud-model auto-load="true"
vn-id="model" vn-id="model"
url="Travels/extraCommunityFilter" url="Travels/extraCommunityFilter"
data="travels"> data="travels"
order="landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, evaNotes"
limit="20">
</vn-crud-model> </vn-crud-model>
<vn-portal slot="topbar"> <vn-portal slot="topbar">
<vn-searchbar <vn-searchbar
vn-focus vn-focus
placeholder="Search by travel id" panel="vn-extra-community-search-panel"
info="Search by travel id" suggested-filter="$ctrl.defaultFilter"
filter="$ctrl.defaultFilter"
info="Search by travel id or reference"
auto-state="false" auto-state="false"
model="model"> model="model">
</vn-searchbar> </vn-searchbar>
</vn-portal> </vn-portal>
<vn-data-viewer model="model"> <vn-data-viewer model="model" class="travel-list">
<vn-card> <vn-card ng-repeat="travel in travels" class="vn-mb-md">
<section ng-repeat="travel in travels" class="vn-pa-md"> <section class="vn-pa-md">
<vn-horizontal class="header"> <vn-table vn-droppable="$ctrl.onDrop($event)" ng-attr-id="{{::travel.id}}">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th>{{travel.id}}</vn-th>
<vn-th>{{travel.supplierNickname}}</vn-th>
</vn-tr>
</vn-thead>
</vn-table>
<!-- <h5><span></span></h5> -->
</vn-horizontal>
<!-- <vn-table>
<vn-thead> <vn-thead>
<vn-tr> <vn-tr ng-if="$index == 0">
<vn-th class="waste-family">Family</vn-th> <vn-th shrink>Id</vn-th>
<vn-th number>Percentage</vn-th> <vn-th expand>Supplier</vn-th>
<vn-th number>Dwindle</vn-th> <vn-th expand>Freighter</vn-th>
<vn-th number>Total</vn-th> <vn-th>Reference</vn-th>
<vn-th number>Packages</vn-th>
<vn-th number>Bl. KG</vn-th>
<vn-th number>Phy. KG</vn-th>
<vn-th number>Vol. KG</vn-th>
<vn-th expand translate-attr="{title: 'Warehouse Out'}">
Wh. Out
</vn-th>
<vn-th expand>Shipped</vn-th>
<vn-th expand translate-attr="{title: 'Warehouse In'}">
Wh. In
</vn-th>
<vn-th expand>Landed</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="waste in detail.lines"> <vn-tr class="header">
<vn-td class="waste-family">{{::waste.family}}</vn-td> <vn-td>
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td> <span class="link"
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td> ng-click="travelDescriptor.show($event, travel.id)">
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td> {{::travel.id}}
</span>
</vn-td>
<vn-td expand>{{::travel.agencyModeName}}</vn-td>
<vn-td expand>{{::travel.cargoSupplierNickname}}</vn-td>
<vn-td-editable expand>
<text>{{travel.ref}}</text>
<field>
<vn-textfield class="dense" vn-focus
ng-model="travel.ref"
on-change="$ctrl.changeReference(travel)">
</vn-textfield>
</field>
</vn-td-editable>
<vn-td number>{{::travel.stickers}}</vn-td>
<vn-td number>{{::travel.kg}}</vn-td>
<vn-td number>{{::travel.loadedKg}}</vn-td>
<vn-td number>{{::travel.volumeKg}}</vn-td>
<vn-td expand>{{::travel.warehouseOutName}}</vn-td>
<vn-td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td expand>{{::travel.warehouseInName}}</vn-td>
<vn-td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</vn-td>
</vn-tr> </vn-tr>
<a href="#" ng-repeat="entry in travel.entries" class="vn-tr" draggable
ng-attr-id="{{::entry.id}}"
ng-click="$event.preventDefault()">
<vn-td>
<span class="link"
ng-click="entryDescriptor.show($event, entry.id)">
{{::entry.id}}
</span>
</vn-td>
<vn-td>{{::entry.supplierName}}</vn-td>
<vn-td></vn-td>
<vn-td expand>{{::entry.ref}}</vn-td>
<vn-th number>{{::entry.stickers}}</vn-th>
<vn-td number></vn-td>
<vn-td number>{{::entry.loadedkg}}</vn-td>
<vn-td number>{{::entry.volumeKg}}</vn-td>
<vn-td>
<span nf-if="::entry.notes" vn-tooltip="{{::entry.notes}}">
{{::entry.notes}}
</span>
</vn-td>
<vn-td>
<span nf-if="::entry.evaNotes" vn-tooltip="{{::entry.evaNotes}}">
{{::entry.evaNotes}}
</span>
</vn-td>
<vn-td></vn-td>
<vn-td></vn-td>
</a>
</vn-tbody> </vn-tbody>
</vn-table> --> </vn-table>
</section> </section>
</vn-card> </vn-card>
</vn-data-viewer> </vn-data-viewer>
<vn-travel-descriptor-popover
vn-id="travelDescriptor">
</vn-travel-descriptor-popover>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>

View File

@ -2,7 +2,90 @@ import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
import './style.scss'; import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
const draggable = this.element.querySelector('.travel-list');
draggable.addEventListener('dragstart',
event => this.dragStart(event));
draggable.addEventListener('dragend',
event => this.dragEnd(event));
this.draggableElement = 'a[draggable]';
this.droppableElement = 'vn-table[vn-droppable]';
const scopeDays = 14;
const landedFrom = new Date();
landedFrom.setHours(0, 0, 0, 0);
const landedTo = new Date();
landedTo.setDate(landedTo.getDate() + scopeDays);
landedTo.setHours(23, 59, 59, 59);
this.defaultFilter = {
landedFrom: landedFrom,
landedTo: landedTo,
continent: 'AM'
};
}
findDraggable($event) {
const target = $event.target;
const draggable = target.closest(this.draggableElement);
return draggable;
}
findDroppable($event) {
const target = $event.target;
const droppable = target.closest(this.droppableElement);
return droppable;
}
dragStart($event) {
const draggable = this.findDraggable($event);
draggable.classList.add('dragging');
const id = draggable.getAttribute('id');
this.entryId = id;
this.entry = draggable;
}
dragEnd($event) {
const draggable = this.findDraggable($event);
draggable.classList.remove('dragging');
this.entryId = null;
this.entry = null;
}
onDrop($event) {
const model = this.$.model;
const droppable = this.findDroppable($event);
const travelId = droppable.getAttribute('id');
const currentDroppable = this.entry.closest(this.droppableElement);
if (currentDroppable == droppable) return;
if (this.entryId && travelId) {
const path = `Entries/${this.entryId}`;
this.$http.patch(path, {travelFk: travelId})
.then(() => model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
}
changeReference(travel) {
const params = {ref: travel.ref};
const endpoint = `Travels/${travel.id}`;
this.$http.patch(endpoint, params)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
}
ngModule.vnComponent('vnTravelExtraCommunity', { ngModule.vnComponent('vnTravelExtraCommunity', {
template: require('./index.html'), template: require('./index.html'),
controller: Section controller: Controller
}); });

View File

@ -0,0 +1,47 @@
import './index.js';
describe('Travel Component vnTravelExtraCommunity', () => {
let controller;
let $httpBackend;
let travel = {
id: 1,
warehouseInFk: 1,
totalEntries: 3,
isDelivered: false
};
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-travel-extra-community></vn-travel-extra-community>');
controller = $componentController('vnTravelExtraCommunity', {$element});
controller.$.summary = {show: jasmine.createSpy('show')};
}));
describe('changeReference()', () => {
it('should make an HTTP query', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const travel = {id: 1, ref: 'New reference'};
const expectedData = {ref: 'New reference'};
$httpBackend.expect('PATCH', `Travels/${travel.id}`, expectedData).respond(200);
controller.changeReference(travel);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
xdescribe('changeReference()', () => {
it('should make an HTTP query', () => {
let event = new MouseEvent('click', {
bubbles: true,
cancelable: true
});
controller.preview(event, travel);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
});

View File

@ -1 +1,8 @@
Family: Familia Family: Familia
Extra community: Extra comunitarios
Freighter: Transitario
Bl. KG: KG Bloq.
Phy. KG: KG físico
Vol. KG: KG Vol.
Search by travel id or reference: Buscar por id travel o referencia
Continent Out: Continente salida

View File

@ -3,7 +3,6 @@
vn-travel-extra-community { vn-travel-extra-community {
.header { .header {
margin-bottom: 16px; margin-bottom: 16px;
text-transform: uppercase;
font-size: 1.25rem; font-size: 1.25rem;
line-height: 1; line-height: 1;
padding: 7px; padding: 7px;
@ -11,15 +10,46 @@ vn-travel-extra-community {
padding-bottom: 4px; padding-bottom: 4px;
font-weight: lighter; font-weight: lighter;
background-color: #fde6ca; background-color: #fde6ca;
color: $color-font-light;
border-bottom: 1px solid #f7931e; border-bottom: 1px solid #f7931e;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
vn-table vn-th.waste-family, vn-td-editable text {
vn-table vn-td.waste-family { background-color: transparent;
max-width: 64px; padding: 0;
width: 64px border: 0;
border-bottom: 1px dashed $color-active;
border-radius: 0;
color: $color-active
}
vn-td-editable text:after {
font-family: 'Material Icons';
content: 'edit';
position: absolute;
margin-left: 5px;
color: $color-spacer
}
vn-table[vn-droppable] {
border-radius: 0;
}
a[draggable] {
transition: all .5s;
cursor: move;
outline: 0;
}
a[draggable]:hover {
background-color: $color-hover-cd
}
a[draggable].dragging {
background-color: $color-success-light;
font-weight:bold
} }
} }

View File

@ -14,3 +14,4 @@ import './thermograph/create/';
import './thermograph/edit/'; import './thermograph/edit/';
import './descriptor-popover'; import './descriptor-popover';
import './extra-community'; import './extra-community';
import './extra-community-search-panel';

View File

@ -93,7 +93,7 @@
"acl": ["buyer"] "acl": ["buyer"]
}, },
{ {
"url": "/extra-community", "url": "/extra-community?q",
"state": "travel.extraCommunity", "state": "travel.extraCommunity",
"component": "vn-travel-extra-community", "component": "vn-travel-extra-community",
"description": "Extra community", "description": "Extra community",

View File

@ -1,6 +1,6 @@
Reference: Referencia Reference: Referencia
Warehouse In: Almacen entrada Warehouse In: Almacén entrada
Warehouse Out: Almacen salida Warehouse Out: Almacén salida
Shipped: F. envío Shipped: F. envío
Landed: F. entrega Landed: F. entrega
Total entries: Entradas totales Total entries: Entradas totales