diff --git a/loopback/server/connectors/vn-mysql.js b/loopback/server/connectors/vn-mysql.js
index 5c625be85b..4e1345cd69 100644
--- a/loopback/server/connectors/vn-mysql.js
+++ b/loopback/server/connectors/vn-mysql.js
@@ -84,6 +84,32 @@ class VnMySQL extends MySQL {
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.
*
diff --git a/modules/entry/back/models/buy.json b/modules/entry/back/models/buy.json
index 56f1eef4f3..65bf15fa49 100644
--- a/modules/entry/back/models/buy.json
+++ b/modules/entry/back/models/buy.json
@@ -34,9 +34,6 @@
"stickers": {
"type": "number"
},
- "packageFk": {
- "type": "number"
- },
"groupingMode": {
"type": "number"
},
@@ -68,6 +65,11 @@
"model": "Item",
"foreignKey": "itemFk",
"required": true
- }
+ },
+ "package": {
+ "type": "belongsTo",
+ "model": "Packaging",
+ "foreignKey": "packageFk"
+ }
}
}
\ No newline at end of file
diff --git a/modules/item/back/methods/item/specs/getDiary.spec.js b/modules/item/back/methods/item/specs/getDiary.spec.js
index cf9ed93204..2903fc426e 100644
--- a/modules/item/back/methods/item/specs/getDiary.spec.js
+++ b/modules/item/back/methods/item/specs/getDiary.spec.js
@@ -5,10 +5,8 @@ describe('item getBalance()', () => {
let params = {where: {itemFk: 1, warehouseFk: 2}};
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[1].balance).toBe(-110);
- expect(result[2].balance).toBe(-110);
- expect(result[3].balance).toBe(-210);
+ expect(result[1].balance).toBe(-200);
});
});
diff --git a/modules/travel/back/methods/travel/extraCommunityFilter.js b/modules/travel/back/methods/travel/extraCommunityFilter.js
new file mode 100644
index 0000000000..4a49b44015
--- /dev/null
+++ b/modules/travel/back/methods/travel/extraCommunityFilter.js
@@ -0,0 +1,213 @@
+
+const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
+const buildFilter = require('vn-loopback/util/filter').buildFilter;
+const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
+
+module.exports = Self => {
+ Self.remoteMethodCtx('extraCommunityFilter', {
+ description: 'Find all instances of the model matched by filter from the data source.',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'filter',
+ type: 'Object',
+ description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
+ http: {source: 'query'}
+ }, {
+ arg: 'search',
+ type: 'String',
+ description: 'Searchs the travel by id',
+ http: {source: 'query'}
+ }, {
+ arg: 'id',
+ type: 'Integer',
+ description: 'The travel id',
+ http: {source: 'query'}
+ }, {
+ arg: 'shippedFrom',
+ type: 'Date',
+ description: 'The shipped from date filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'shippedTo',
+ type: 'Date',
+ description: 'The shipped to date filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'landedFrom',
+ type: 'Date',
+ description: 'The landed from date filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'landedTo',
+ type: 'Date',
+ description: 'The landed to date filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'agencyFk',
+ type: 'Number',
+ description: 'The agencyModeFk id',
+ http: {source: 'query'}
+ }, {
+ arg: 'warehouseOutFk',
+ type: 'Number',
+ description: 'The warehouseOutFk filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'warehouseInFk',
+ type: 'Number',
+ description: 'The warehouseInFk filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'totalEntries',
+ type: 'Number',
+ description: 'The totalEntries filter',
+ http: {source: 'query'}
+ }, {
+ arg: 'ref',
+ type: 'string',
+ description: 'The reference'
+ }, {
+ arg: 'continent',
+ type: 'string',
+ description: 'The continent code'
+ }
+ ],
+ returns: {
+ type: ['Object'],
+ root: true
+ },
+ http: {
+ path: `/extraCommunityFilter`,
+ verb: 'GET'
+ }
+ });
+
+ Self.extraCommunityFilter = async(ctx, filter) => {
+ let conn = Self.dataSource.connector;
+ let where = buildFilter(ctx.args, (param, value) => {
+ switch (param) {
+ case 'search':
+ return /^\d+$/.test(value)
+ ? {'t.id': value}
+ : {'t.ref': {like: `%${value}%`}};
+ case 'ref':
+ return {'t.ref': {like: `%${value}%`}};
+ case 'shippedFrom':
+ return {'t.shipped': {gte: value}};
+ case 'shippedTo':
+ return {'t.shipped': {lte: value}};
+ case 'landedFrom':
+ return {'t.landed': {gte: value}};
+ case 'landedTo':
+ return {'t.landed': {lte: value}};
+ case 'continent':
+ return {'cnt.code': value};
+ case 'id':
+ case 'agencyFk':
+ case 'warehouseOutFk':
+ case 'warehouseInFk':
+ case 'totalEntries':
+ param = `t.${param}`;
+ return {[param]: value};
+ }
+ });
+
+ filter = mergeFilters(filter, {where});
+
+ let stmts = [];
+ let stmt;
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.travel');
+ stmt = new ParameterizedSQL(
+ `CREATE TEMPORARY TABLE tmp.travel
+ (INDEX (id))
+ ENGINE = MEMORY
+ SELECT
+ t.id,
+ t.ref,
+ t.shipped,
+ t.landed,
+ t.kg,
+ am.id AS agencyModeFk,
+ am.name AS agencyModeName,
+ wo.id AS warehouseOutFk,
+ wo.name AS warehouseOutName,
+ w.name AS warehouseInFk,
+ w.name AS warehouseInName,
+ SUM(b.stickers) AS stickers,
+ s.id AS supplierFk,
+ s.nickname AS cargoSupplierNickname,
+ 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 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 packaging pkg ON pkg.id = b.packageFk
+ LEFT JOIN item i ON i.id = b.itemFk
+ LEFT JOIN itemType it ON it.id = i.typeFk
+ JOIN warehouse w ON w.id = t.warehouseInFk
+ JOIN warehouse wo ON wo.id = t.warehouseOutFk
+ 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`
+ );
+
+ stmt.merge(conn.makeWhere(filter.where));
+ stmt.merge(conn.makeGroupBy('t.id'));
+ stmt.merge(conn.makeLimit(filter));
+ stmts.push(stmt);
+
+ const travelsIndex = stmts.push('SELECT * FROM tmp.travel') - 1;
+
+ stmt = new ParameterizedSQL(
+ `SELECT
+ 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 result = await conn.executeStmt(sql);
+
+ const travels = result[travelsIndex];
+ const entries = result[entriesIndex];
+
+ const travelsMap = new Map();
+ for (let travel of travels)
+ travelsMap.set(travel.id, travel);
+
+ for (let entry of entries) {
+ const travel = travelsMap.get(entry.travelFk);
+
+ if (travel) {
+ if (!travel.entries) travel.entries = [];
+
+ travel.entries.push(entry);
+ }
+ }
+
+ return travels;
+ };
+};
+
diff --git a/modules/travel/back/methods/travel/specs/extraCommunityFilter.spec.js b/modules/travel/back/methods/travel/specs/extraCommunityFilter.spec.js
new file mode 100644
index 0000000000..7f42eb5f06
--- /dev/null
+++ b/modules/travel/back/methods/travel/specs/extraCommunityFilter.spec.js
@@ -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);
+ });
+});
diff --git a/modules/travel/back/methods/travel/specs/filter.spec.js b/modules/travel/back/methods/travel/specs/filter.spec.js
index d04b0f0931..ababe961e6 100644
--- a/modules/travel/back/methods/travel/specs/filter.spec.js
+++ b/modules/travel/back/methods/travel/specs/filter.spec.js
@@ -38,7 +38,7 @@ describe('Travel filter()', () => {
const result = await app.models.Travel.filter(ctx);
- expect(result.length).toEqual(8);
+ expect(result.length).toEqual(3);
});
it('should return the travel matching "total entries"', async() => {
diff --git a/modules/travel/back/models/travel.js b/modules/travel/back/models/travel.js
index b47742c263..b8a1a24b36 100644
--- a/modules/travel/back/models/travel.js
+++ b/modules/travel/back/models/travel.js
@@ -7,6 +7,7 @@ module.exports = Self => {
require('../methods/travel/createThermograph')(Self);
require('../methods/travel/deleteThermograph')(Self);
require('../methods/travel/updateThermograph')(Self);
+ require('../methods/travel/extraCommunityFilter')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
diff --git a/modules/travel/front/descriptor/locale/es.yml b/modules/travel/front/descriptor/locale/es.yml
index 0ae18fdbf9..3e6d627353 100644
--- a/modules/travel/front/descriptor/locale/es.yml
+++ b/modules/travel/front/descriptor/locale/es.yml
@@ -1,6 +1,6 @@
Reference: Referencia
-Wh. In: Almacén entrada
-Wh. Out: Almacén salida
+Wh. In: Alm. entrada
+Wh. Out: Alm. salida
Shipped: F. envío
Landed: F. entrega
Total entries: Entradas totales
\ No newline at end of file
diff --git a/modules/travel/front/extra-community-search-panel/index.html b/modules/travel/front/extra-community-search-panel/index.html
new file mode 100644
index 0000000000..85a30907a3
--- /dev/null
+++ b/modules/travel/front/extra-community-search-panel/index.html
@@ -0,0 +1,91 @@
+
+
+
\ No newline at end of file
diff --git a/modules/travel/front/extra-community-search-panel/index.js b/modules/travel/front/extra-community-search-panel/index.js
new file mode 100644
index 0000000000..63d404b4b1
--- /dev/null
+++ b/modules/travel/front/extra-community-search-panel/index.js
@@ -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
+});
diff --git a/modules/travel/front/extra-community/index.html b/modules/travel/front/extra-community/index.html
new file mode 100644
index 0000000000..84e6fcb854
--- /dev/null
+++ b/modules/travel/front/extra-community/index.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/travel/front/extra-community/index.js b/modules/travel/front/extra-community/index.js
new file mode 100644
index 0000000000..221d6cb0d0
--- /dev/null
+++ b/modules/travel/front/extra-community/index.js
@@ -0,0 +1,91 @@
+import ngModule from '../module';
+import Section from 'salix/components/section';
+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 = parseInt(draggable.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 = parseInt(droppable.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', {
+ template: require('./index.html'),
+ controller: Controller
+});
diff --git a/modules/travel/front/extra-community/index.spec.js b/modules/travel/front/extra-community/index.spec.js
new file mode 100644
index 0000000000..2f510f22fc
--- /dev/null
+++ b/modules/travel/front/extra-community/index.spec.js
@@ -0,0 +1,127 @@
+import './index.js';
+
+describe('Travel Component vnTravelExtraCommunity', () => {
+ let controller;
+ let $httpBackend;
+
+ beforeEach(ngModule('travel'));
+
+ beforeEach(inject(($componentController, _$httpBackend_) => {
+ $httpBackend = _$httpBackend_;
+ const $element = angular.element('');
+ controller = $componentController('vnTravelExtraCommunity', {$element});
+ controller.$.model = {};
+ controller.$.model.refresh = jest.fn();
+ }));
+
+ 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!');
+ });
+ });
+
+ describe('findDraggable()', () => {
+ it('should find the draggable element', () => {
+ const draggable = document.createElement('a');
+ draggable.setAttribute('draggable', true);
+
+ const $event = new Event('dragstart');
+ const target = document.createElement('div');
+ draggable.appendChild(target);
+ target.dispatchEvent($event);
+
+ const result = controller.findDraggable($event);
+
+ expect(result).toEqual(draggable);
+ });
+ });
+
+ describe('findDroppable()', () => {
+ it('should find the droppable element', () => {
+ const droppable = document.createElement('vn-table');
+ droppable.setAttribute('vn-droppable', true);
+
+ const $event = new Event('drop');
+ const target = document.createElement('div');
+ droppable.appendChild(target);
+ target.dispatchEvent($event);
+
+ const result = controller.findDroppable($event);
+
+ expect(result).toEqual(droppable);
+ });
+ });
+
+ describe('dragStart()', () => {
+ it(`should add the class "dragging" to the draggable element
+ and then set the entryId controller property`, () => {
+ const draggable = document.createElement('a');
+ draggable.setAttribute('draggable', true);
+ draggable.setAttribute('id', 3);
+
+ jest.spyOn(controller, 'findDraggable').mockReturnValue(draggable);
+
+ const $event = new Event('dragStart');
+ controller.dragStart($event);
+
+ const firstClass = draggable.classList[0];
+
+ expect(firstClass).toEqual('dragging');
+ expect(controller.entryId).toEqual(3);
+ expect(controller.entry).toEqual(draggable);
+ });
+ });
+
+ describe('dragEnd()', () => {
+ it(`should remove the class "dragging" from the draggable element
+ and then set the entryId controller property to null`, () => {
+ const draggable = document.createElement('a');
+ draggable.setAttribute('draggable', true);
+ draggable.setAttribute('id', 3);
+ draggable.classList.add('dragging');
+
+ jest.spyOn(controller, 'findDraggable').mockReturnValue(draggable);
+
+ const $event = new Event('dragStart');
+ controller.dragEnd($event);
+
+ const classList = draggable.classList;
+
+ expect(classList.length).toEqual(0);
+ expect(controller.entryId).toBeNull();
+ expect(controller.entry).toBeNull();
+ });
+ });
+
+ describe('onDrop()', () => {
+ it('should make an HTTP patch query', () => {
+ const droppable = document.createElement('vn-table');
+ droppable.setAttribute('vn-droppable', true);
+ droppable.setAttribute('id', 1);
+
+ jest.spyOn(controller, 'findDroppable').mockReturnValue(droppable);
+
+ const oldDroppable = document.createElement('vn-table');
+ oldDroppable.setAttribute('vn-droppable', true);
+ const entry = document.createElement('div');
+ oldDroppable.appendChild(entry);
+
+ controller.entryId = 3;
+ controller.entry = entry;
+
+ const $event = new Event('drop');
+ const expectedData = {travelFk: 1};
+ $httpBackend.expect('PATCH', `Entries/3`, expectedData).respond(200);
+ controller.onDrop($event);
+ $httpBackend.flush();
+ });
+ });
+});
diff --git a/modules/travel/front/extra-community/locale/es.yml b/modules/travel/front/extra-community/locale/es.yml
new file mode 100644
index 0000000000..e377a10111
--- /dev/null
+++ b/modules/travel/front/extra-community/locale/es.yml
@@ -0,0 +1,8 @@
+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
\ No newline at end of file
diff --git a/modules/travel/front/extra-community/style.scss b/modules/travel/front/extra-community/style.scss
new file mode 100644
index 0000000000..e7265781dd
--- /dev/null
+++ b/modules/travel/front/extra-community/style.scss
@@ -0,0 +1,55 @@
+@import "variables";
+
+vn-travel-extra-community {
+ .header {
+ margin-bottom: 16px;
+ font-size: 1.25rem;
+ line-height: 1;
+ padding: 7px;
+ padding-bottom: 7px;
+ padding-bottom: 4px;
+ font-weight: lighter;
+ background-color: #fde6ca;
+ color: $color-font-light;
+ border-bottom: 1px solid #f7931e;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ vn-td-editable text {
+ background-color: transparent;
+ padding: 0;
+ 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
+ }
+}
\ No newline at end of file
diff --git a/modules/travel/front/index.js b/modules/travel/front/index.js
index 52a09af30c..e4375c59da 100644
--- a/modules/travel/front/index.js
+++ b/modules/travel/front/index.js
@@ -14,3 +14,5 @@ import './thermograph/create/';
import './thermograph/edit/';
import './descriptor-popover';
import './descriptor-menu';
+import './extra-community';
+import './extra-community-search-panel';
diff --git a/modules/travel/front/routes.json b/modules/travel/front/routes.json
index 5fa43fd1d3..b2e438c6d1 100644
--- a/modules/travel/front/routes.json
+++ b/modules/travel/front/routes.json
@@ -6,7 +6,8 @@
"dependencies": ["worker", "entry"],
"menus": {
"main": [
- {"state": "travel.index", "icon": "local_airport"}
+ {"state": "travel.index", "icon": "local_airport"},
+ {"state": "travel.extraCommunity", "icon": "directions_boat"}
],
"card": [
{"state": "travel.card.basicData", "icon": "settings"},
@@ -90,6 +91,16 @@
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
+ },
+ {
+ "url": "/extra-community?q",
+ "state": "travel.extraCommunity",
+ "component": "vn-travel-extra-community",
+ "description": "Extra community",
+ "acl": ["buyer"],
+ "params": {
+ "travel": "$ctrl.travel"
+ }
}
]
}
\ No newline at end of file
diff --git a/modules/travel/front/summary/locale/es.yml b/modules/travel/front/summary/locale/es.yml
index 09e5a19634..aa002fad05 100644
--- a/modules/travel/front/summary/locale/es.yml
+++ b/modules/travel/front/summary/locale/es.yml
@@ -1,6 +1,6 @@
Reference: Referencia
-Warehouse In: Almacen entrada
-Warehouse Out: Almacen salida
+Warehouse In: Almacén entrada
+Warehouse Out: Almacén salida
Shipped: F. envío
Landed: F. entrega
Total entries: Entradas totales
diff --git a/package-lock.json b/package-lock.json
index 2fcd0e4403..99e20a441d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5264,6 +5264,16 @@
"integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==",
"dev": true
},
+ "@types/yauzl": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
+ "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -8546,6 +8556,12 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
+ "devtools-protocol": {
+ "version": "0.0.818844",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
+ "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==",
+ "dev": true
+ },
"diff": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
@@ -9857,47 +9873,40 @@
}
},
"extract-zip": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
- "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"requires": {
- "concat-stream": "^1.6.2",
- "debug": "^2.6.9",
- "mkdirp": "^0.5.4",
+ "@types/yauzl": "^2.9.1",
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"dependencies": {
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
+ "ms": "2.1.2"
}
},
- "fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"requires": {
- "pend": "~1.2.0"
+ "pump": "^3.0.0"
}
},
- "yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
- "dev": true,
- "requires": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
}
}
},
@@ -9981,6 +9990,15 @@
"bser": "^2.0.0"
}
},
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
"feature-policy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz",
@@ -21828,9 +21846,9 @@
}
},
"proxy-from-env": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
- "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true
},
"prr": {
@@ -21914,70 +21932,68 @@
}
},
"puppeteer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-2.0.0.tgz",
- "integrity": "sha512-t3MmTWzQxPRP71teU6l0jX47PHXlc4Z52sQv4LJQSZLq1ttkKS2yGM3gaI57uQwZkNaoGd0+HPPMELZkcyhlqA==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz",
+ "integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==",
"dev": true,
"requires": {
"debug": "^4.1.0",
- "extract-zip": "^1.6.6",
- "https-proxy-agent": "^3.0.0",
- "mime": "^2.0.3",
+ "devtools-protocol": "0.0.818844",
+ "extract-zip": "^2.0.0",
+ "https-proxy-agent": "^4.0.0",
+ "node-fetch": "^2.6.1",
+ "pkg-dir": "^4.2.0",
"progress": "^2.0.1",
"proxy-from-env": "^1.0.0",
- "rimraf": "^2.6.1",
- "ws": "^6.1.0"
+ "rimraf": "^3.0.2",
+ "tar-fs": "^2.0.0",
+ "unbzip2-stream": "^1.3.3",
+ "ws": "^7.2.3"
},
"dependencies": {
+ "agent-base": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
+ "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==",
+ "dev": true
+ },
"debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
- "ms": "^2.1.1"
+ "ms": "2.1.2"
}
},
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
}
},
"https-proxy-agent": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
- "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
+ "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
"dev": true,
"requires": {
- "agent-base": "^4.3.0",
- "debug": "^3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- }
+ "agent-base": "5",
+ "debug": "4"
}
},
- "mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
- "dev": true
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
},
"ms": {
"version": "2.1.2",
@@ -21985,14 +22001,44 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.0.0"
+ }
+ },
"rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
+ },
+ "ws": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
+ "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==",
+ "dev": true
}
}
},
@@ -25143,6 +25189,40 @@
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
},
+ "unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ },
+ "dependencies": {
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ }
+ }
+ },
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -26908,6 +26988,16 @@
"dev": true
}
}
+ },
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+ "dev": true,
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
}
}
}
diff --git a/package.json b/package.json
index c594514669..1a301afb0f 100644
--- a/package.json
+++ b/package.json
@@ -85,7 +85,7 @@
"node-sass": "^4.14.1",
"nodemon": "^1.19.4",
"plugin-error": "^1.0.1",
- "puppeteer": "^2.0.0",
+ "puppeteer": "^5.5.0",
"raw-loader": "^1.0.0",
"sass-loader": "^7.3.1",
"style-loader": "^0.23.1",