From 3d21539fb009cfa189f1ecb85236f4c69da66da2 Mon Sep 17 00:00:00 2001 From: Bernat Exposito Domenech Date: Fri, 20 Dec 2019 07:55:00 +0100 Subject: [PATCH] travel index filter --- front/core/components/searchbar/searchbar.js | 1 - modules/travel/back/methods/travel/filter.js | 146 ++++++++++++++++++ .../back/methods/travel/specs/filter.spec.js | 58 +++++++ modules/travel/back/models/travel.js | 1 + modules/travel/front/index/index.html | 20 +-- modules/travel/front/index/index.js | 61 +++----- modules/travel/front/index/index.spec.js | 57 +++++-- modules/travel/front/search-panel/index.html | 2 +- 8 files changed, 283 insertions(+), 63 deletions(-) create mode 100644 modules/travel/back/methods/travel/filter.js create mode 100644 modules/travel/back/methods/travel/specs/filter.spec.js diff --git a/front/core/components/searchbar/searchbar.js b/front/core/components/searchbar/searchbar.js index 98f52ee36..a82324877 100644 --- a/front/core/components/searchbar/searchbar.js +++ b/front/core/components/searchbar/searchbar.js @@ -254,7 +254,6 @@ class AutoSearch { } } } - this.model.applyFilter( where ? {where} : null, hasParams ? userParams : null diff --git a/modules/travel/back/methods/travel/filter.js b/modules/travel/back/methods/travel/filter.js new file mode 100644 index 000000000..6cd0caed4 --- /dev/null +++ b/modules/travel/back/methods/travel/filter.js @@ -0,0 +1,146 @@ + +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('filter', { + 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: 'm3', + type: 'Number', + description: 'The m3 filter', + 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'} + } + ], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/filter`, + verb: 'GET' + } + }); + + Self.filter = async(ctx, filter) => { + let conn = Self.dataSource.connector; + let where = buildFilter(ctx.args, (param, value) => { + switch (param) { + case 'search': + return {'t.id': value}; + case 'ref': + return {[param]: {regexp: 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 'id': + case 'agencyFk': + case 'warehouseOutFk': + case 'warehouseInFk': + case 'totalEntries': + param = `t.${param}`; + return {[param]: value}; + } + }); + + filter = mergeFilters(ctx.args.filter, {where}); + + let stmts = []; + let stmt; + stmt = new ParameterizedSQL( + `SELECT + t.id, + t.shipped, + t.landed, + t.warehouseInFk, + t.warehouseOutFk, + t.agencyFk, + t.ref, + t.isDelivered, + t.isReceived, + t.m3, + t.kg, + t.cargoSupplierFk, + t.totalEntries, + am.name agencyModeName, + win.name warehouseInName, + wout.name warehouseOutName + FROM vn.travel t + JOIN vn.agencyMode am ON am.id = t.agencyFk + JOIN vn.warehouse win ON win.id = t.warehouseInFk + JOIN vn.warehouse wout ON wout.id = t.warehouseOutFk` + ); + + + stmt.merge(conn.makeSuffix(filter)); + let itemsIndex = stmts.push(stmt) - 1; + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await conn.executeStmt(sql); + return itemsIndex === 0 ? result : result[itemsIndex]; + }; +}; + diff --git a/modules/travel/back/methods/travel/specs/filter.spec.js b/modules/travel/back/methods/travel/specs/filter.spec.js new file mode 100644 index 000000000..f2fe44989 --- /dev/null +++ b/modules/travel/back/methods/travel/specs/filter.spec.js @@ -0,0 +1,58 @@ +const app = require('vn-loopback/server/server'); + +describe('Travel filter()', () => { + it('should return the travel matching "search"', async() => { + let ctx = { + args: { + search: 1 + } + }; + + let result = await app.models.Travel.filter(ctx); + + expect(result.length).toEqual(1); + expect(result[0].id).toEqual(1); + }); + + it('should return the travel matching "warehouse out"', async() => { + let ctx = { + args: { + warehouseOutFk: 2 + } + }; + + let result = await app.models.Travel.filter(ctx); + + expect(result.length).toEqual(7); + }); + + it('should return the travel matching "total entries"', async() => { + let ctx = { + args: { + totalEntries: 1, + } + }; + + let result = await app.models.Travel.filter(ctx); + + expect(result.length).toEqual(5); + }); + + it('should return the routes matching "shipped from" and "shipped 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() + 1); + let ctx = { + args: { + shippedFrom: from, + shippedTo: to + } + }; + + let result = await app.models.Travel.filter(ctx); + + expect(result.length).toEqual(1); + }); +}); diff --git a/modules/travel/back/models/travel.js b/modules/travel/back/models/travel.js index 936b68cd9..5fa55a366 100644 --- a/modules/travel/back/models/travel.js +++ b/modules/travel/back/models/travel.js @@ -1,4 +1,5 @@ module.exports = Self => { require('../methods/travel/getTravel')(Self); require('../methods/travel/getEntries')(Self); + require('../methods/travel/filter')(Self); }; diff --git a/modules/travel/front/index/index.html b/modules/travel/front/index/index.html index 044f1ca8f..3af99eb6b 100644 --- a/modules/travel/front/index/index.html +++ b/modules/travel/front/index/index.html @@ -1,17 +1,17 @@ + params="::$ctrl.params" + data="travels" + order="shipped DESC, landed DESC"> + on-search="$ctrl.onSearch($params)"> + class="vn-mb-xl vn-w-xl"> @@ -34,11 +34,11 @@ ui-sref="travel.card.summary({id: {{::travel.id}}})"> {{::travel.id}} {{::travel.ref}} - {{::travel.agency.name}} - {{::travel.warehouseOut.name}} + {{::travel.agencyModeName}} + {{::travel.warehouseOutName}} {{::travel.shipped | date:'dd/MM/yyyy'}} - {{::travel.warehouseIn.name}} + {{::travel.warehouseInName}} {{::travel.landed | date:'dd/MM/yyyy'}} @@ -51,8 +51,8 @@ - + diff --git a/modules/travel/front/index/index.js b/modules/travel/front/index/index.js index a4dd60d77..a1e22d2e7 100644 --- a/modules/travel/front/index/index.js +++ b/modules/travel/front/index/index.js @@ -4,44 +4,6 @@ export default class Controller { constructor($scope) { this.$ = $scope; this.ticketSelected = null; - - this.filter = { - include: [ - { - relation: 'agency', - scope: {fields: ['name']} - }, { - relation: 'warehouseIn', - scope: {fields: ['name']} - }, { - relation: 'warehouseOut', - scope: {fields: ['name']} - } - ] - }; - } - - exprBuilder(param, value) { - switch (param) { - case 'search': - return {id: value}; - case 'ref': - return {[param]: {regexp: value}}; - case 'shippedFrom': - return {shipped: {gte: value}}; - case 'shippedTo': - return {shipped: {lte: value}}; - case 'landedFrom': - return {landed: {gte: value}}; - case 'landedTo': - return {landed: {lte: value}}; - case 'id': - case 'agencyModeFk': - case 'warehouseOutFk': - case 'warehouseInFk': - case 'totalEntries': - return {[param]: value}; - } } preview(event, travel) { @@ -50,6 +12,29 @@ export default class Controller { event.preventDefault(); event.stopImmediatePropagation(); } + + getScopeDates(days) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + const daysOnward = new Date(today); + daysOnward.setDate(today.getDate() + days); + daysOnward.setHours(23, 59, 59, 999); + return {shippedFrom: today, shippedTo: daysOnward}; + } + + onSearch(params) { + if (params) { + let newParams = params; + if (params.scopeDays) { + const scopeDates = this.getScopeDates(params.scopeDays); + Object.assign(newParams, scopeDates); + } else if (Object.entries(params).length == 0) + newParams = this.getScopeDates(1); + + this.$.model.applyFilter(null, newParams); + } else + this.$.model.clear(); + } } Controller.$inject = ['$scope']; diff --git a/modules/travel/front/index/index.spec.js b/modules/travel/front/index/index.spec.js index 321109003..5affc7c1a 100644 --- a/modules/travel/front/index/index.spec.js +++ b/modules/travel/front/index/index.spec.js @@ -3,31 +3,62 @@ import './index.js'; describe('Travel Component vnTravelIndex', () => { let $componentController; let controller; + let $window; + let travels = [{ + id: 1, + warehouseInFk: 1, + totalEntries: 3, + isDelivered: false + }, { + id: 2, + warehouseInFk: 1, + total: 4, + isDelivered: true + }, { + id: 3, + warehouseInFk: 1, + total: 2, + isDelivered: true + }]; beforeEach(angular.mock.module('travel', $translateProvider => { $translateProvider.translations('en', {}); })); - beforeEach(angular.mock.inject(_$componentController_ => { + beforeEach(angular.mock.inject((_$componentController_, $rootScope) => { $componentController = _$componentController_; - controller = $componentController('vnTravelIndex'); + let $scope = $rootScope.$new(); + controller = $componentController('vnTravelIndex', $scope); + controller.$.summary = {show: jasmine.createSpy('show')}; })); - describe('exprBuilder()', () => { - it('should return a formated object with the travel id in case of search', () => { - let param = 'search'; - let value = 2; - let result = controller.exprBuilder(param, value); + describe('preview()', () => { + it('should show the dialog summary', () => { + let event = new MouseEvent('click', { + view: $window, + bubbles: true, + cancelable: true + }); + controller.preview(event, travels[0]); - expect(result).toEqual({id: 2}); + expect(controller.$.summary.show).toHaveBeenCalledWith(); }); + }); - it('should return a formated object with the warehouseInFk in case of warehouseInFk', () => { - let param = 'warehouseInFk'; - let value = 3; - let result = controller.exprBuilder(param, value); + describe('getScopeDates()', () => { + it('should return a range of dates', () => { + let days = 2; // never put 1 or anything higher than 2 + let result = controller.getScopeDates(days); - expect(result).toEqual({warehouseInFk: 3}); + let from = new Date(result.shippedFrom).getTime(); + let to = new Date(result.shippedTo).getTime(); + let range = to - from; + + const dayInMilliseconds = 24 * 60 * 60 * 1000; + + let millisecondsPerAddedDay = dayInMilliseconds - 1; + + expect(range - dayInMilliseconds).toEqual(dayInMilliseconds + millisecondsPerAddedDay); }); }); }); diff --git a/modules/travel/front/search-panel/index.html b/modules/travel/front/search-panel/index.html index bd68fd151..afc041f66 100644 --- a/modules/travel/front/search-panel/index.html +++ b/modules/travel/front/search-panel/index.html @@ -30,7 +30,7 @@