Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
gitea/salix/dev There was a failure building this commit Details

This commit is contained in:
Joan Sanchez 2019-04-16 07:59:15 +02:00
commit f814584f6b
24 changed files with 531 additions and 41 deletions

View File

@ -31,4 +31,3 @@ rules:
curly: [error, multi-or-nest]
indent: [error, 4]
arrow-parens: [error, as-needed]
jasmine/no-focused-tests: 0

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

@ -65,13 +65,13 @@ class Controller {
}
onMoreOpen() {
let options = this.moreOptions.filter(o => {
const hasShowProperty = Object.hasOwnProperty.call(o, 'show');
const hasAclProperty = Object.hasOwnProperty.call(o, 'acl');
const hasAcl = !hasAclProperty || (hasAclProperty && this.aclService.hasAny([o.acl]));
let options = this.moreOptions.filter(option => {
const hasShowProperty = Object.hasOwnProperty.call(option, 'show');
const hasAclProperty = Object.hasOwnProperty.call(option, 'acl');
const hasAcl = !hasAclProperty || (hasAclProperty && this.aclService.hasAny([option.acl]));
return (!hasShowProperty || o.show === true ||
typeof o.show === 'function' && o.show()) && hasAcl;
return (!hasShowProperty || option.show === true ||
typeof option.show === 'function' && option.show()) && hasAcl;
});
this.$scope.moreButton.data = options;
}

View File

@ -62,6 +62,7 @@
<vn-td number>
<vn-check vn-one
field="::request.isOk"
triple-state="true"
disabled="true">
</vn-check>
</vn-td>

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