travel index filter
gitea/salix/1897travel.index This commit looks good
Details
gitea/salix/1897travel.index This commit looks good
Details
This commit is contained in:
parent
688b68a18a
commit
3d21539fb0
|
@ -254,7 +254,6 @@ class AutoSearch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.applyFilter(
|
this.model.applyFilter(
|
||||||
where ? {where} : null,
|
where ? {where} : null,
|
||||||
hasParams ? userParams : null
|
hasParams ? userParams : null
|
||||||
|
|
|
@ -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];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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'];
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue