#1339 Print - Hoja de rutas

This commit is contained in:
Carlos Jimenez Ruiz 2019-04-15 14:34:33 +02:00
parent f5fd929970
commit 82ed0078d2
22 changed files with 525 additions and 35 deletions

View File

@ -440,9 +440,12 @@ INSERT INTO `vn`.`ticket`(`id`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped
INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`)
VALUES
( 1, 1 , 1, 'ready' ),
( 2, 2 , 2, 'do it fast please'),
( 3, 3 , 3, '');
(1, 1 , 1, 'ready' ),
(2, 2 , 2, 'do it fast please'),
(3, 3 , 3, 'Faster faster fasteeeeeer!!!'),
(4, 4 , 3, 'Deliver before 8am'),
(5, 13 , 3, 'You can run from the disappointments you are trying to forget. But its only when you embrace your past that you truly move forward. Maybe I never get to go home again, but I found my way there. And I am glad I did.'),
(6, 14, 3, 'Careful, armed warhead');
INSERT INTO `vn`.`ticketTracking`(`id`, `ticketFk`, `stateFk`, `workerFk`, `created`)
VALUES

View File

@ -6,6 +6,15 @@
<a translate-attr="{title: 'Preview'}" ui-sref="route.card.summary({id: $ctrl.route.id})">
<vn-icon icon="desktop_windows"></vn-icon>
</a>
<vn-icon-menu
vn-id="more-button"
icon="more_vert"
show-filter="false"
value-field="callback"
translate-fields="['name']"
data="$ctrl.moreOptions"
on-change="$ctrl.onMoreChange(value)">
</vn-icon-menu>
</div>
<div class="body">
<div class="attributes">

View File

@ -1,6 +1,16 @@
import ngModule from '../module';
class Controller {
constructor($, $http, vnApp, $translate) {
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
this.$ = $;
this.moreOptions = [
{callback: this.showRouteReport, name: 'Show route report'},
{callback: this.sendRouteReport, name: 'Send route report'}
];
}
set quicklinks(value = {}) {
this._quicklinks = Object.assign(value, this._quicklinks);
}
@ -8,9 +18,25 @@ class Controller {
get quicklinks() {
return this._quicklinks;
}
onMoreChange(callback) {
callback.call(this);
}
showRouteReport() {
let url = `/api/report/rpt-route?routeFk=${this.route.id}`;
window.open(url);
}
sendRouteReport() {
let url = `/api/email/driver-route?routeFk=${this.route.id}`;
this.$http.post(url).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Report sent'));
});
}
}
Controller.$inject = ['$http', '$state'];
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];
ngModule.component('vnRouteDescriptor', {
template: require('./index.html'),

View File

@ -1,2 +1,4 @@
Volume exceded: Volumen excedido
Volume: Volumen
Send route report: Enviar informe de ruta
Show route report: Ver informe de ruta

View File

@ -63,8 +63,8 @@ class Controller {
}
onMoreOpen() {
let options = this.moreOptions.filter(o => {
return o.show === true || typeof o.show === 'function' && o.show();
let options = this.moreOptions.filter(option => {
return option.show === true || typeof option.show === 'function' && option.show();
});
this.$scope.moreButton.data = options;
}

View File

@ -99,6 +99,11 @@
width: 35.4px
}
.field.rectangle span {
height: 2em;
width: 8em
}
.pull-left {
float: left !important
}

View File

@ -10,6 +10,14 @@
text-align: center
}
.align-right {
text-align: right
}
.align-left {
text-align: left
}
.number {
text-align: right
}
@ -29,3 +37,7 @@
.font.bold {
font-weight: bold
}
.non-page-break {
page-break-inside: avoid;
}

View File

@ -6,12 +6,13 @@
{"type": "email", "name": "letter-debtor-nd"},
{"type": "email", "name": "claim-pickup-order"},
{"type": "email", "name": "sepa-core"},
{"type": "email", "name": "driver-route"},
{"type": "report", "name": "rpt-delivery-note"},
{"type": "report", "name": "rpt-invoice"},
{"type": "report", "name": "rpt-claim-pickup-order"},
{"type": "report", "name": "rpt-letter-debtor"},
{"type": "report", "name": "rpt-sepa-core"},
{"type": "report", "name": "rpt-receipt"},
{"type": "report", "name": "rpt-zone"},
{"type": "report", "name": "rpt-route"},
{"type": "static", "name": "email-header"},
{"type": "static", "name": "email-footer"},

View File

@ -0,0 +1,7 @@
const CssReader = require(`${appPath}/lib/cssReader`);
module.exports = new CssReader([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/email.css`,
`${appPath}/common/css/misc.css`])
.mergeStyles();

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="es">
<head>
<title>{{ $t('subject') }}</title>
</head>
<body>
<section class="container">
<!-- Header component -->
<email-header></email-header>
<!-- End header component -->
<section class="main">
<!-- Title block -->
<div class="title">
<h1>{{ $t('title') }}</h1>
</div>
<!-- Title block end -->
<p>{{$t('description.instructions')}}</p>
</section>
<!-- Footer component -->
<email-footer :locale="locale"></email-footer>
<!-- End footer component -->
</section>
</body>
</html>

View File

@ -0,0 +1,54 @@
const UserException = require(`${appPath}/lib/exceptions/userException`);
const reportEngine = require(`${appPath}/lib/reportEngine`);
const database = require(`${appPath}/lib/database`);
const emailHeader = require('../email-header');
const emailFooter = require('../email-footer');
module.exports = {
name: 'driver-route',
async asyncData(ctx, params) {
const promises = [];
const data = {
isPreview: ctx.method === 'GET',
};
if (!params.routeFk)
throw new UserException('No route id specified');
promises.push(reportEngine.toPdf('rpt-route', ctx));
promises.push(this.methods.fetchRoute(params.routeFk));
return Promise.all(promises).then(result => {
const stream = result[0];
const [[route]] = result[1];
Object.assign(data, route);
Object.assign(data, {attachments: [{filename: 'driver-route.pdf', content: stream}]});
return data;
});
},
created() {
if (this.locale)
this.$i18n.locale = this.locale;
},
methods: {
fetchRoute(routeFk) {
return database.pool.query(`
SELECT
u.id,
u.lang AS locale,
mu.email AS recipient
FROM route r
LEFT JOIN worker w ON w.id = r.workerFk
LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN account.emailUser mu ON mu.userFk = u.id
WHERE r.id = ?`, [routeFk]);
},
},
components: {
emailHeader,
emailFooter,
},
};

View File

@ -0,0 +1,11 @@
module.exports = {
messages: {
es: {
subject: 'Hoja de ruta',
title: 'Hoja de ruta',
description: {
instructions: 'Adjuntamos tu hoja de ruta.'
},
},
},
};

View File

@ -3,5 +3,6 @@ const CssReader = require(`${appPath}/lib/cssReader`);
module.exports = new CssReader([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${appPath}/common/css/misc.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -1,9 +1,49 @@
section .text {
font-family: Tahoma;
font-weight: bold;
color: white;
font-size: 7.5em;
h1 {
text-align: center;
background-color: black;
margin-bottom: 0.2em
}
th.align-right {
padding-right: 1em;
}
.gap {
width: 3em;
}
.contained {
padding-top: 20px;
}
.middle {
margin: auto;
}
p.small {
width: 8em;
}
.black-container {
padding-top: 0.19em;
padding-right: 0.2em;
padding-left: 0.2em;
background-color: black;
color: white;
font-size: 1.3em;
}
table.repeatable {
margin-top: 1em;
margin-bottom: 1em;
}
table.repeatable > tbody > tr > td {
padding-top: 0.5em;
}
section.text-area {
margin-top: 1em;
padding: 0.19em;
padding-left: 1em;
padding-right: 1em;
background-color: #e5e5e5;
}

View File

@ -2,14 +2,155 @@
<html lang="es">
<body>
<section class="container" id="report">
<!-- Header component -->
<report-header :locale="route.locale"></report-header>
<!-- End header component -->
<section class="main">
<!-- Report start -->
<section class="text">{{route.agencyName}}</section>
<section class="text">{{route.id}}</section>
<section class="text">{{route.plateNumber}} {{routeTime(route.time)}}</section>
<section></section>
<!-- Report end -->
<h1 class="title uppercase">{{$t('Title')}}</h1>
<section class="panel">
<section class="header">{{$t('Information')}}</section>
<section class="body">
<section>
<table width="100%">
<tbody>
<tr>
<th class="font gray align-right">{{$t('Route')}}</th>
<td>{{route.id}}</td>
<th class="font gray align-right">{{$t('Driver')}}</th>
<td>{{route.userNickName}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Date')}}</th>
<td>{{date(route.created)}}</td>
<th class="font gray align-right">{{$t('Vehicle')}}</th>
<td>{{route.vehicleTradeMark}} {{route.vehicleModel}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Time')}}</th>
<td>{{time(route.time)}}</td>
<td></td>
<td>{{route.plateNumber}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Volume')}}</th>
<td>{{route.m3}}</td>
<th class="font gray align-right">{{$t('Agency')}}</th>
<td>{{route.agencyName}}</td>
</tr>
</tbody>
</table>
<section class="contained">
<table class="middle centered" width="70%">
<tbody>
<tr>
<td>
<p class="small">Hora inicio</p>
</td>
<td>
<p class="small">Hora fin</p>
</td>
<td class="gap"></td>
<td>
<p class="small">Km inicio</p>
</td>
<td>
<p class="small">Km fin</p>
</td>
</tr>
<tr>
<td v-for="i in 2">
<section class="field rectangle">
<span></span>
</section>
</td>
<td class="gap"></td>
<td v-for="i in 2">
<section class="field rectangle">
<span></span>
</section>
</td>
</tr>
</tbody>
</table>
</section>
</section>
</section>
</section>
<section class="non-page-break" v-for="ticket in tickets">
<section>
<table class="column-oriented repeatable">
<thead>
<tr>
<td class="number">{{$t('Order')}}</td>
<td class="number">{{$t('Ticket')}}</td>
<td>{{$t('Client')}}</td>
<td class="number">{{$t('Address')}}</td>
<td class="number">{{$t('Packages')}}</td>
</tr>
</thead>
<tbody>
<tr>
<td class="number">{{ticket.priority}}</td>
<td class="number">{{ticket.id}}</td>
<td>{{ticket.clientFk}} {{ticket.addressName}}</td>
<td v-if="ticket.addressFk" class="number">
{{ticket.addressFk.toString().substr(0, ticket.addressFk.toString().length - 3)}}
<span class="black-container">
{{ticket.addressFk.toString().substr(-3, 3)}}
</span>
</td>
<td class="number">{{ticket.packages}}</td>
</tr>
</tbody>
</table>
</section>
<section>
<table width="100%">
<tbody>
<tr>
<th class="font gray align-right">{{$t('Street')}}</th>
<td>{{ticket.street}}</td>
<th class="font gray align-right">{{$t('Postcode')}}</th>
<td>{{ticket.postalCode}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('City')}}</th>
<td>{{ticket.city}}</td>
<th class="font gray align-right">{{$t('Agency')}}</th>
<td>{{ticket.ticketAgency}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Mobile')}}</th>
<td>{{ticket.mobile}}</td>
<th class="font gray align-right">{{$t('Phone')}}</th>
<td>{{ticket.phone}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Warehouse')}}</th>
<td>{{ticket.warehouseName}}</td>
<th class="font gray align-right">{{$t('salesPerson')}}</th>
<td>{{ticket.salesPersonName}}</td>
</tr>
<tr>
<th class="font gray align-right">{{$t('Import')}}</th>
<td>{{ticket.import}}</td>
</tr>
</tbody>
</table>
<section v-if="ticket.description || ticket.shipFk" class="text-area">
<p>{{ticket.description}}</p>
<p v-if="ticket.shipFk">{{$t('stowaway')}}: {{ticket.shipFk}}</p>
</section>
</section>
</section>
</section>
</section>
<!-- Footer component -->
<report-footer id="pageFooter"
:left-text="$t('route', [route.id])"
:locale="route.locale">
</report-footer>
<!-- End footer component -->
</section>
</body>
</html>

View File

@ -5,32 +5,90 @@ const UserException = require(`${appPath}/lib/exceptions/userException`);
module.exports = {
name: 'rpt-route',
async asyncData(ctx, params) {
if (!params.routeFk)
throw new UserException('No route id specified');
Object.assign(this, this.methods);
let [[route]] = await this.methods.fetchRoute(params.routeFk);
const [[route]] = await this.fetchRoute(params.routeFk);
const [tickets] = await this.fetchTickets(params.routeFk);
if (!route)
throw new UserException('Route not ready');
throw new UserException('No route data found');
return {route};
if (!tickets)
throw new UserException('No ticket data found');
return {route, tickets};
},
methods: {
fetchRoute(routeFk) {
return database.pool.query(
`SELECT
r.id,
r.m3,
r.created,
r.time,
am.name agencyName,
v.numberPlate plateNumber
u.nickName userNickName,
u.lang AS locale,
v.tradeMark vehicleTradeMark,
v.model vehicleModel,
v.numberPlate plateNumber,
am.name agencyName
FROM route r
JOIN agencyMode am ON am.id = r.agencyModeFk
JOIN vehicle v ON v.id = r.vehicleFk
LEFT JOIN ticket t ON t.routeFk = r.id
LEFT JOIN sale s ON s.ticketFk = t.id
LEFT JOIN cache.last_buy lb ON lb.item_id = s.itemFk
LEFT JOIN vehicle v ON v.id = r.vehicleFk
LEFT JOIN worker w ON w.id = r.workerFk
LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN agencyMode am ON am.id = r.agencyModeFk
WHERE r.id = ?`, [routeFk]);
},
routeTime: routeTime => {
if (routeTime)
return strftime('%H:%M', routeTime);
fetchTickets(routeFk) {
return database.pool.query(
`SELECT
t.nickname addressName,
t.packages,
t.priority,
t.id,
t.clientFk,
t.companyFk,
if(a.phone, a.phone, c.phone) AS phone,
if(a.mobile, a.mobile, c.mobile) AS mobile,
wh.name warehouseName,
a.city,
a.street,
a.postalCode,
LPAD(a.id, 5, '0') AS addressFk,
p.name province,
vn.ticketGetTotal(t.id) AS import,
am.name ticketAgency,
tob.description,
s.shipFk,
u.nickName salesPersonName
FROM route r
LEFT JOIN ticket t ON t.routeFk = r.id
LEFT JOIN address a ON a.id = t.addressFk
LEFT JOIN client c ON c.id = t.clientFk
LEFT JOIN worker w ON w.id = vn2008.Averiguar_ComercialCliente_Id(t.clientFk, CURDATE())
LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id AND tob.observationTypeFk = 3
LEFT JOIN province p ON a.provinceFk = p.id
LEFT JOIN warehouse wh ON wh.id = t.warehouseFk
LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
LEFT JOIN stowaway s ON s.id = t.id
WHERE r.id = ?
ORDER BY t.priority, t.id`, [routeFk]);
},
date(date) {
if (date)
return strftime('%d-%m-%Y', date);
},
time: time => {
if (time)
return strftime('%H:%M', time);
},
},
components: {
'report-header': require('../report-header'),
'report-footer': require('../report-footer'),
},
};

View File

@ -0,0 +1,29 @@
module.exports = {
messages: {
es: {
Title: 'Hoja de ruta',
Information: 'Información',
Route: 'Ruta',
Date: 'Fecha',
Time: 'Hora',
Volume: 'Cubicaje',
Driver: 'Conductor',
Vehicle: 'Vehículo',
Agency: 'Agencia',
Order: 'Orden',
Client: 'Cliente',
Address: 'Consignatario',
Packages: 'Bultos',
Street: 'Dirección',
Postcode: 'Código Postal',
City: 'Ciudad',
Mobile: 'Móvil',
Phone: 'Teléfono',
Warehouse: 'Almacén',
salesPerson: 'Comercial',
Import: 'Importe',
stowaway: 'Encajado dentro del ticket',
route: 'Ruta {0}'
}
},
};

View File

@ -0,0 +1,7 @@
const CssReader = require(`${appPath}/lib/cssReader`);
module.exports = new CssReader([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,9 @@
section .text {
font-family: Tahoma;
font-weight: bold;
color: white;
font-size: 7.5em;
text-align: center;
background-color: black;
margin-bottom: 0.2em
}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<body>
<section class="container" id="report">
<section class="main">
<!-- Report start -->
<section class="text">{{zone.agencyName}}</section>
<section class="text">{{zone.id}}</section>
<section class="text">{{zone.plateNumber}} {{zoneTime(zone.time)}}</section>
<section></section>
<!-- Report end -->
</section>
</section>
</body>
</html>

36
print/report/rpt-zone/index.js Executable file
View File

@ -0,0 +1,36 @@
const strftime = require('strftime');
const database = require(`${appPath}/lib/database`);
const UserException = require(`${appPath}/lib/exceptions/userException`);
module.exports = {
name: 'rpt-zone',
async asyncData(ctx, params) {
if (!params.routeFk)
throw new UserException('No route id specified');
let [[zone]] = await this.methods.fetchRoute(params.routeFk);
if (!zone)
throw new UserException('Route not ready');
return {zone};
},
methods: {
fetchRoute(routeFk) {
return database.pool.query(
`SELECT
r.id,
r.time,
am.name agencyName,
v.numberPlate plateNumber
FROM route r
JOIN agencyMode am ON am.id = r.agencyModeFk
JOIN vehicle v ON v.id = r.vehicleFk
WHERE r.id = ?`, [routeFk]);
},
zoneTime: zoneTime => {
if (zoneTime)
return strftime('%H:%M', zoneTime);
},
},
};

View File