travel index filter
gitea/salix/1897travel.index This commit looks good Details

This commit is contained in:
Bernat Exposito Domenech 2019-12-20 07:55:00 +01:00
parent 688b68a18a
commit 3d21539fb0
8 changed files with 283 additions and 63 deletions

View File

@ -254,7 +254,6 @@ class AutoSearch {
} }
} }
} }
this.model.applyFilter( this.model.applyFilter(
where ? {where} : null, where ? {where} : null,
hasParams ? userParams : null hasParams ? userParams : null

View File

@ -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];
};
};

View File

@ -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);
});
});

View File

@ -1,4 +1,5 @@
module.exports = Self => { module.exports = Self => {
require('../methods/travel/getTravel')(Self); require('../methods/travel/getTravel')(Self);
require('../methods/travel/getEntries')(Self); require('../methods/travel/getEntries')(Self);
require('../methods/travel/filter')(Self);
}; };

View File

@ -1,17 +1,17 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="Travels" url="Travels/filter"
filter="::$ctrl.filter"
limit="20" limit="20"
data="travels"> params="::$ctrl.params"
data="travels"
order="shipped DESC, landed DESC">
</vn-crud-model> </vn-crud-model>
<vn-auto-search <vn-auto-search
model="model" on-search="$ctrl.onSearch($params)">
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-auto-search> </vn-auto-search>
<vn-data-viewer <vn-data-viewer
model="model" model="model"
class="vn-w-lg"> class="vn-mb-xl vn-w-xl">
<vn-card> <vn-card>
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>
@ -34,11 +34,11 @@
ui-sref="travel.card.summary({id: {{::travel.id}}})"> ui-sref="travel.card.summary({id: {{::travel.id}}})">
<vn-td number>{{::travel.id}}</vn-td> <vn-td number>{{::travel.id}}</vn-td>
<vn-td expand>{{::travel.ref}}</vn-td> <vn-td expand>{{::travel.ref}}</vn-td>
<vn-td expand>{{::travel.agency.name}}</vn-td> <vn-td expand>{{::travel.agencyModeName}}</vn-td>
<vn-td expand>{{::travel.warehouseOut.name}}</vn-td> <vn-td expand>{{::travel.warehouseOutName}}</vn-td>
<vn-td center>{{::travel.shipped | date:'dd/MM/yyyy'}}</vn-td> <vn-td center>{{::travel.shipped | date:'dd/MM/yyyy'}}</vn-td>
<vn-td><vn-check ng-model="travel.isDelivered" disabled="true"></vn-check></vn-td> <vn-td><vn-check ng-model="travel.isDelivered" disabled="true"></vn-check></vn-td>
<vn-td expand>{{::travel.warehouseIn.name}}</vn-td> <vn-td expand>{{::travel.warehouseInName}}</vn-td>
<vn-td center>{{::travel.landed | date:'dd/MM/yyyy'}}</vn-td> <vn-td center>{{::travel.landed | date:'dd/MM/yyyy'}}</vn-td>
<vn-td center><vn-check ng-model="travel.isReceived" disabled="true"></vn-check></vn-td> <vn-td center><vn-check ng-model="travel.isReceived" disabled="true"></vn-check></vn-td>
<vn-td> <vn-td>
@ -51,8 +51,8 @@
</a> </a>
</vn-tbody> </vn-tbody>
</vn-table> </vn-table>
</vn-card>
</vn-data-viewer> </vn-data-viewer>
</vn-card>
<vn-popup vn-id="summary"> <vn-popup vn-id="summary">
<vn-travel-summary <vn-travel-summary
travel="$ctrl.travelSelected"> travel="$ctrl.travelSelected">

View File

@ -4,44 +4,6 @@ export default class Controller {
constructor($scope) { constructor($scope) {
this.$ = $scope; this.$ = $scope;
this.ticketSelected = null; 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) { preview(event, travel) {
@ -50,6 +12,29 @@ export default class Controller {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); 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']; Controller.$inject = ['$scope'];

View File

@ -3,31 +3,62 @@ import './index.js';
describe('Travel Component vnTravelIndex', () => { describe('Travel Component vnTravelIndex', () => {
let $componentController; let $componentController;
let controller; 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 => { beforeEach(angular.mock.module('travel', $translateProvider => {
$translateProvider.translations('en', {}); $translateProvider.translations('en', {});
})); }));
beforeEach(angular.mock.inject(_$componentController_ => { beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
$componentController = _$componentController_; $componentController = _$componentController_;
controller = $componentController('vnTravelIndex'); let $scope = $rootScope.$new();
controller = $componentController('vnTravelIndex', $scope);
controller.$.summary = {show: jasmine.createSpy('show')};
})); }));
describe('exprBuilder()', () => { describe('preview()', () => {
it('should return a formated object with the travel id in case of search', () => { it('should show the dialog summary', () => {
let param = 'search'; let event = new MouseEvent('click', {
let value = 2; view: $window,
let result = controller.exprBuilder(param, value); 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', () => { describe('getScopeDates()', () => {
let param = 'warehouseInFk'; it('should return a range of dates', () => {
let value = 3; let days = 2; // never put 1 or anything higher than 2
let result = controller.exprBuilder(param, value); 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);
}); });
}); });
}); });

View File

@ -30,7 +30,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
label="Agency" label="Agency"
ng-model="filter.agencyFk" ng-model="filter.agencyFk"
url="Agencies" url="AgencyModes"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>