Merge pull request 'Download route PDF on route index' (#357) from 2358-route_download_pdf into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #357
Reviewed-by: Carlos Jimenez <carlosjr@verdnatura.es>
This commit is contained in:
Joan Sanchez 2020-08-25 13:17:58 +00:00
commit 17cd808015
10 changed files with 166 additions and 23 deletions

View File

@ -371,7 +371,7 @@ export default {
ticketsIndex: { ticketsIndex: {
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"]', advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"]',
newTicketButton: 'vn-ticket-index a', newTicketButton: 'vn-ticket-index a[ui-sref="ticket.create"]',
searchResult: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr', searchResult: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
secondTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(2) > vn-td:nth-child(1) > vn-check', secondTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(2) > vn-td:nth-child(1) > vn-check',
thirdTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(3) > vn-td:nth-child(1) > vn-check', thirdTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(3) > vn-td:nth-child(1) > vn-check',
@ -689,7 +689,7 @@ export default {
confirmButton: '.vn-confirm.shown button[response="accept"]', confirmButton: '.vn-confirm.shown button[response="accept"]',
}, },
routeIndex: { routeIndex: {
addNewRouteButton: 'vn-route-index > a[ui-sref="route.create"]' addNewRouteButton: 'vn-route-index a[ui-sref="route.create"]'
}, },
createRouteView: { createRouteView: {
worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]', worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',

View File

@ -8,6 +8,11 @@
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</vn-th>
<vn-th field="id" number>Id</vn-th> <vn-th field="id" number>Id</vn-th>
<vn-th th-id="worker">Worker</vn-th> <vn-th th-id="worker">Worker</vn-th>
<vn-th th-id="agency">Agency</vn-th> <vn-th th-id="agency">Agency</vn-th>
@ -22,6 +27,12 @@
<a ng-repeat="route in model.data" <a ng-repeat="route in model.data"
class="clickable vn-tr search-result" class="clickable vn-tr search-result"
ui-sref="route.card.summary({id: {{::route.id}}})"> ui-sref="route.card.summary({id: {{::route.id}}})">
<vn-td shrink>
<vn-check
ng-model="route.checked"
vn-click-stop>
</vn-check>
</vn-td>
<vn-td number>{{::route.id | dashIfEmpty}}</vn-td> <vn-td number>{{::route.id | dashIfEmpty}}</vn-td>
<vn-td expand> <vn-td expand>
<span <span
@ -55,9 +66,26 @@
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor"> vn-id="workerDescriptor">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<a ui-sref="route.create"
vn-tooltip="New route"
vn-bind="+" </vn-data-viewer>
fixed-bottom-right> <div fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-vertical style="align-items: center;">
</a> <vn-button class="round sm vn-mb-sm"
icon="cloud_download"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.showRouteReport()"
vn-tooltip="Download selected routes as PDF"
tooltip-position="left">
</vn-button>
<a ui-sref="route.create">
<vn-button class="round md vn-mb-sm"
icon="add"
vn-bind="+"
vn-tooltip="New route"
tooltip-position="left">
</vn-button>
</a>
</vn-vertical>
</div>

View File

@ -2,12 +2,46 @@ import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
export default class Controller extends Section { export default class Controller extends Section {
constructor($element, $, vnReport) {
super($element, $);
this.vnReport = vnReport;
}
preview(route) { preview(route) {
this.routeSelected = route; this.routeSelected = route;
this.$.summary.show(); this.$.summary.show();
} }
get checked() {
const rows = this.$.model.data || [];
const checkedRows = [];
for (let row of rows) {
if (row.checked)
checkedRows.push(row);
}
return checkedRows;
}
get totalChecked() {
return this.checked.length;
}
showRouteReport() {
const routes = [];
for (let route of this.checked)
routes.push(route.id);
const routesId = routes.join(',');
this.vnReport.show('driver-route', {
authorization: this.vnToken.token,
routeId: routesId
});
}
} }
Controller.$inject = ['$element', '$scope', 'vnReport'];
ngModule.vnComponent('vnRouteIndex', { ngModule.vnComponent('vnRouteIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller controller: Controller

View File

@ -0,0 +1,60 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Component vnRouteIndex', () => {
let controller;
beforeEach(ngModule('route'));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-route-index></vn-route-index>');
controller = $componentController('vnRouteIndex', {$element});
controller.$.model = crudModel;
controller.$.model.data = [{id: 1}, {id: 2}, {id: 3}];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
data[2].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
const secondCheckedRow = checkedRows[1];
expect(firstCheckedRow.id).toEqual(1);
expect(secondCheckedRow.id).toEqual(3);
});
});
describe('totalCheked() getter', () => {
it('should return the total checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
const checkedRows = controller.totalChecked;
expect(checkedRows).toEqual(1);
});
});
describe('showRouteReport()', () => {
it('should call to the vnReport show method', () => {
controller.vnReport.show = jest.fn();
const data = controller.$.model.data;
data[0].checked = true;
data[2].checked = true;
const expectedParams = {
authorization: null,
routeId: '1,3'
};
controller.showRouteReport();
expect(controller.vnReport.show).toHaveBeenCalledWith('driver-route', expectedParams);
});
});
});

View File

@ -1 +1,2 @@
Vehicle: Vehículo Vehicle: Vehículo
Download selected routes as PDF: Descargar rutas seleccionadas como PDF

View File

@ -24,8 +24,9 @@ p.privacy {
text-align: center text-align: center
} }
.page .pageCount { .pageCount {
text-align: right text-align: right;
float: right
} }
.footer .page > div { .footer .page > div {

View File

@ -33,6 +33,7 @@ class Report extends Component {
args: ['--no-sandbox', '--disable-setuid-sandbox'] args: ['--no-sandbox', '--disable-setuid-sandbox']
}); });
const page = await browser.newPage(); const page = await browser.newPage();
await page.emulateMedia('screen');
await page.setContent(template); await page.setContent(template);
const element = await page.$('#pageFooter'); const element = await page.$('#pageFooter');

View File

@ -47,3 +47,8 @@ section.text-area {
padding-right: 1em; padding-right: 1em;
background-color: #e5e5e5; background-color: #e5e5e5;
} }
.route-block {
margin-bottom: 100px;
page-break-after: always;
}

View File

@ -8,9 +8,9 @@
<!-- Header block --> <!-- Header block -->
<report-header v-bind="$props"></report-header> <report-header v-bind="$props"></report-header>
<!-- Block --> <!-- Block -->
<div class="grid-row"> <div class="grid-row route-block" v-for="route in routes">
<div class="grid-block"> <div class="grid-block">
<h1 class="title uppercase">{{route.id}}</h1> <h1 class="title uppercase">{{$t('route')}} {{route.id}}</h1>
<div class="panel"> <div class="panel">
<div class="header">{{$t('information')}}</div> <div class="header">{{$t('information')}}</div>
<div class="body"> <div class="body">
@ -80,7 +80,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="no-page-break" v-for="ticket in tickets"> <!-- Route ticket list -->
<div class="no-page-break" v-for="ticket in route.tickets">
<div> <div>
<table class="column-oriented repeatable"> <table class="column-oriented repeatable">
<thead> <thead>
@ -151,7 +152,7 @@
</div> </div>
<!-- Footer block --> <!-- Footer block -->
<report-footer id="pageFooter" <report-footer id="pageFooter"
v-bind:left-text="$t('routeId', [route.id])" v-bind:left-text="$t('routeId', [routeId])"
v-bind="$props"> v-bind="$props">
</report-footer> </report-footer>
</td> </td>

View File

@ -6,15 +6,26 @@ const reportFooter = new Component('report-footer');
module.exports = { module.exports = {
name: 'driver-route', name: 'driver-route',
async serverPrefetch() { async serverPrefetch() {
this.route = await this.fetchRoute(this.routeId); const routesId = this.routeId.split(',');
this.tickets = await this.fetchTickets(this.routeId); const routes = await this.fetchRoutes(routesId);
const tickets = await this.fetchTickets(routesId);
if (!this.route) for (let route of routes) {
const routeTickets = tickets.filter(ticket => {
return ticket.routeFk == route.id;
});
route.tickets = routeTickets;
}
this.routes = routes;
if (!this.routes)
throw new Error('Something went wrong'); throw new Error('Something went wrong');
}, },
methods: { methods: {
fetchRoute(id) { fetchRoutes(routesId) {
return db.findOne( return db.rawSql(
`SELECT `SELECT
r.id, r.id,
r.m3, r.m3,
@ -30,9 +41,9 @@ module.exports = {
LEFT JOIN worker w ON w.id = r.workerFk LEFT JOIN worker w ON w.id = r.workerFk
LEFT JOIN account.user u ON u.id = w.userFk LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN agencyMode am ON am.id = r.agencyModeFk LEFT JOIN agencyMode am ON am.id = r.agencyModeFk
WHERE r.id = :routeId`, {routeId: id}); WHERE r.id IN(:routesId)`, {routesId});
}, },
fetchTickets(routeId) { fetchTickets(routesId) {
return db.rawSql( return db.rawSql(
`SELECT `SELECT
t.nickname addressName, t.nickname addressName,
@ -41,6 +52,7 @@ module.exports = {
t.id, t.id,
t.clientFk, t.clientFk,
t.companyFk, t.companyFk,
t.routeFk,
if(a.phone, a.phone, c.phone) AS phone, if(a.phone, a.phone, c.phone) AS phone,
if(a.mobile, a.mobile, c.mobile) AS mobile, if(a.mobile, a.mobile, c.mobile) AS mobile,
wh.name warehouseName, wh.name warehouseName,
@ -65,8 +77,8 @@ module.exports = {
LEFT JOIN warehouse wh ON wh.id = t.warehouseFk LEFT JOIN warehouse wh ON wh.id = t.warehouseFk
LEFT JOIN agencyMode am ON am.id = t.agencyModeFk LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
LEFT JOIN stowaway s ON s.id = t.id LEFT JOIN stowaway s ON s.id = t.id
WHERE r.id = ? WHERE r.id IN(:routesId)
ORDER BY t.priority, t.id`, [routeId]); ORDER BY t.priority, t.id`, {routesId});
} }
}, },
components: { components: {