5995-newCmr #1698
|
@ -0,0 +1,36 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('cmr', {
|
||||||
|
description: 'Returns the cmr',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The cmr id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/:id/cmr',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.cmr = (ctx, id) => Self.printReport(ctx, id, 'cmr');
|
||||||
|
};
|
|
@ -14,6 +14,7 @@ module.exports = Self => {
|
||||||
require('../methods/route/driverRouteEmail')(Self);
|
require('../methods/route/driverRouteEmail')(Self);
|
||||||
require('../methods/route/sendSms')(Self);
|
require('../methods/route/sendSms')(Self);
|
||||||
require('../methods/route/downloadZip')(Self);
|
require('../methods/route/downloadZip')(Self);
|
||||||
|
require('../methods/route/cmr')(Self);
|
||||||
|
|
||||||
Self.validate('kmStart', validateDistance, {
|
Self.validate('kmStart', validateDistance, {
|
||||||
message: 'Distance must be lesser than 1000'
|
message: 'Distance must be lesser than 1000'
|
||||||
|
@ -28,5 +29,5 @@ module.exports = Self => {
|
||||||
const routeMaxKm = 1000;
|
const routeMaxKm = 1000;
|
||||||
if (routeTotalKm > routeMaxKm || this.kmStart > this.kmEnd)
|
if (routeTotalKm > routeMaxKm || this.kmStart > this.kmEnd)
|
||||||
err();
|
err();
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,177 +5,177 @@ const config = require('vn-print/core/config');
|
||||||
const storage = require('vn-print/core/storage');
|
const storage = require('vn-print/core/storage');
|
||||||
|
|
||||||
module.exports = async function(ctx, Self, tickets, reqArgs = {}) {
|
module.exports = async function(ctx, Self, tickets, reqArgs = {}) {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
if (tickets.length == 0) return;
|
if (tickets.length == 0) return;
|
||||||
|
|
||||||
const failedtickets = [];
|
const failedtickets = [];
|
||||||
for (const ticket of tickets) {
|
for (const ticket of tickets) {
|
||||||
try {
|
try {
|
||||||
await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId});
|
await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId});
|
||||||
|
|
||||||
const [invoiceOut] = await Self.rawSql(`
|
const [invoiceOut] = await Self.rawSql(`
|
||||||
SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued
|
SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
JOIN invoiceOut io ON io.ref = t.refFk
|
JOIN invoiceOut io ON io.ref = t.refFk
|
||||||
JOIN company cny ON cny.id = io.companyFk
|
JOIN company cny ON cny.id = io.companyFk
|
||||||
WHERE t.id = ?
|
WHERE t.id = ?
|
||||||
`, [ticket.id]);
|
`, [ticket.id]);
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
overrideAttachments: true,
|
overrideAttachments: true,
|
||||||
attachments: []
|
attachments: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed;
|
const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed;
|
||||||
|
|
||||||
if (invoiceOut) {
|
if (invoiceOut) {
|
||||||
const args = {
|
const args = {
|
||||||
reference: invoiceOut.ref,
|
reference: invoiceOut.ref,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const invoiceReport = new Report('invoice', args);
|
const invoiceReport = new Report('invoice', args);
|
||||||
const stream = await invoiceReport.toPdfStream();
|
const stream = await invoiceReport.toPdfStream();
|
||||||
|
|
||||||
const issued = invoiceOut.issued;
|
const issued = invoiceOut.issued;
|
||||||
const year = issued.getFullYear().toString();
|
const year = issued.getFullYear().toString();
|
||||||
const month = (issued.getMonth() + 1).toString();
|
const month = (issued.getMonth() + 1).toString();
|
||||||
const day = issued.getDate().toString();
|
const day = issued.getDate().toString();
|
||||||
|
|
||||||
const fileName = `${year}${invoiceOut.ref}.pdf`;
|
const fileName = `${year}${invoiceOut.ref}.pdf`;
|
||||||
|
|
||||||
// Store invoice
|
// Store invoice
|
||||||
await storage.write(stream, {
|
await storage.write(stream, {
|
||||||
type: 'invoice',
|
type: 'invoice',
|
||||||
path: `${year}/${month}/${day}`,
|
path: `${year}/${month}/${day}`,
|
||||||
fileName: fileName
|
fileName: fileName
|
||||||
});
|
});
|
||||||
|
|
||||||
await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId});
|
await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId});
|
||||||
|
|
||||||
if (isToBeMailed) {
|
if (isToBeMailed) {
|
||||||
const invoiceAttachment = {
|
const invoiceAttachment = {
|
||||||
filename: fileName,
|
filename: fileName,
|
||||||
content: stream
|
content: stream
|
||||||
};
|
};
|
||||||
|
|
||||||
if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') {
|
if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') {
|
||||||
const exportation = new Report('exportation', args);
|
const exportation = new Report('exportation', args);
|
||||||
const stream = await exportation.toPdfStream();
|
const stream = await exportation.toPdfStream();
|
||||||
const fileName = `CITES-${invoiceOut.ref}.pdf`;
|
const fileName = `CITES-${invoiceOut.ref}.pdf`;
|
||||||
|
|
||||||
mailOptions.attachments.push({
|
mailOptions.attachments.push({
|
||||||
filename: fileName,
|
filename: fileName,
|
||||||
content: stream
|
content: stream
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mailOptions.attachments.push(invoiceAttachment);
|
mailOptions.attachments.push(invoiceAttachment);
|
||||||
|
|
||||||
const email = new Email('invoice', args);
|
const email = new Email('invoice', args);
|
||||||
await email.send(mailOptions);
|
await email.send(mailOptions);
|
||||||
}
|
}
|
||||||
} else if (isToBeMailed) {
|
} else if (isToBeMailed) {
|
||||||
const args = {
|
const args = {
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const email = new Email('delivery-note-link', args);
|
const email = new Email('delivery-note-link', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incoterms authorization
|
// Incoterms authorization
|
||||||
const [{firstOrder}] = await Self.rawSql(`
|
const [{firstOrder}] = await Self.rawSql(`
|
||||||
SELECT COUNT(*) as firstOrder
|
SELECT COUNT(*) as firstOrder
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
JOIN client c ON c.id = t.clientFk
|
JOIN client c ON c.id = t.clientFk
|
||||||
WHERE t.clientFk = ?
|
WHERE t.clientFk = ?
|
||||||
AND NOT t.isDeleted
|
AND NOT t.isDeleted
|
||||||
AND c.isVies
|
AND c.isVies
|
||||||
`, [ticket.clientFk]);
|
`, [ticket.clientFk]);
|
||||||
|
|
||||||
if (firstOrder == 1) {
|
if (firstOrder == 1) {
|
||||||
const args = {
|
const args = {
|
||||||
id: ticket.clientFk,
|
id: ticket.clientFk,
|
||||||
companyId: ticket.companyFk,
|
companyId: ticket.companyFk,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const email = new Email('incoterms-authorization', args);
|
const email = new Email('incoterms-authorization', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
|
|
||||||
const [sample] = await Self.rawSql(
|
const [sample] = await Self.rawSql(
|
||||||
`SELECT id
|
`SELECT id
|
||||||
FROM sample
|
FROM sample
|
||||||
WHERE code = 'incoterms-authorization'
|
WHERE code = 'incoterms-authorization'
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?)
|
INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?)
|
||||||
`, [ticket.clientFk, sample.id, ticket.companyFk], {userId});
|
`, [ticket.clientFk, sample.id, ticket.companyFk], {userId});
|
||||||
}
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Domain not found
|
// Domain not found
|
||||||
if (error.responseCode == 450)
|
if (error.responseCode == 450)
|
||||||
return invalidEmail(ticket);
|
return invalidEmail(ticket);
|
||||||
|
|
||||||
// Save tickets on a list of failed ids
|
// Save tickets on a list of failed ids
|
||||||
failedtickets.push({
|
failedtickets.push({
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
stacktrace: error
|
stacktrace: error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send email with failed tickets
|
// Send email with failed tickets
|
||||||
if (failedtickets.length > 0) {
|
if (failedtickets.length > 0) {
|
||||||
let body = 'This following tickets have failed:<br/><br/>';
|
let body = 'This following tickets have failed:<br/><br/>';
|
||||||
|
|
||||||
for (const ticket of failedtickets) {
|
for (const ticket of failedtickets) {
|
||||||
body += `Ticket: <strong>${ticket.id}</strong>
|
body += `Ticket: <strong>${ticket.id}</strong>
|
||||||
<br/> <strong>${ticket.stacktrace}</strong><br/><br/>`;
|
<br/> <strong>${ticket.stacktrace}</strong><br/><br/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
smtp.send({
|
smtp.send({
|
||||||
to: config.app.reportEmail,
|
to: config.app.reportEmail,
|
||||||
subject: '[API] Nightly ticket closure report',
|
subject: '[API] Nightly ticket closure report',
|
||||||
html: body
|
html: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function invalidEmail(ticket) {
|
async function invalidEmail(ticket) {
|
||||||
await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
|
await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
|
||||||
ticket.clientFk
|
ticket.clientFk
|
||||||
], {userId});
|
], {userId});
|
||||||
|
|
||||||
const oldInstance = `{"email": "${ticket.recipient}"}`;
|
const oldInstance = `{"email": "${ticket.recipient}"}`;
|
||||||
const newInstance = `{"email": ""}`;
|
const newInstance = `{"email": ""}`;
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
|
INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
|
||||||
VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
|
VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
|
||||||
ticket.clientFk,
|
ticket.clientFk,
|
||||||
oldInstance,
|
oldInstance,
|
||||||
newInstance
|
newInstance
|
||||||
], {userId});
|
], {userId});
|
||||||
|
|
||||||
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
||||||
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
||||||
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta
|
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta
|
||||||
o no está disponible.<br/><br/>
|
o no está disponible.<br/><br/>
|
||||||
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
||||||
Actualiza la dirección de email con una correcta.`;
|
Actualiza la dirección de email con una correcta.`;
|
||||||
|
|
||||||
smtp.send({
|
smtp.send({
|
||||||
to: ticket.salesPersonEmail,
|
to: ticket.salesPersonEmail,
|
||||||
subject: 'No se ha podido enviar el albarán',
|
subject: 'No se ha podido enviar el albarán',
|
||||||
html: body
|
html: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const vnPrintPath = path.resolve('print');
|
||||||
|
|
||||||
|
module.exports = new Stylesheet([
|
||||||
|
`${vnPrintPath}/common/css/spacing.css`,
|
||||||
|
`${vnPrintPath}/common/css/misc.css`,
|
||||||
|
`${vnPrintPath}/common/css/layout.css`,
|
||||||
|
`${vnPrintPath}/common/css/report.css`,
|
||||||
|
`${__dirname}/style.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,101 @@
|
||||||
|
html {
|
||||||
|
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||||
|
margin: 10px;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
.mainTable, .specialTable, .categoryTable {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.mainTable td {
|
||||||
|
width: 50%;
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.signTable {
|
||||||
|
height: 12%;
|
||||||
|
}
|
||||||
|
.signTable td {
|
||||||
|
width: calc(100% / 3);
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 15px;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
#title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 85px;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
height: 0px;
|
||||||
|
border-radius: 25px;
|
||||||
|
}
|
||||||
|
#cellHeader {
|
||||||
|
border: 0px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
#label, #merchandiseLabels {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#merchandiseLabels {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.imgSection {
|
||||||
|
text-align: center;
|
||||||
|
height: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#lineBreak {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
.specialTable td {
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: inherit;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.specialTable #itemCategoryList {
|
||||||
|
width: 70%;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.categoryTable {
|
||||||
|
padding-bottom: none;
|
||||||
|
}
|
||||||
|
.categoryTable td {
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: inherit;
|
||||||
|
border: none;
|
||||||
|
padding: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.categoryTable #merchandiseLabels {
|
||||||
|
border-bottom: 4px solid #cccccc;
|
||||||
|
padding: none;
|
||||||
|
}
|
||||||
|
#merchandiseDetail {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
#merchandiseData {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
#merchandiseLabels td {
|
||||||
|
padding-bottom: 11px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<table class="mainTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">1. Remitente / Expediteur / Sender</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.senderName}}</b><br>
|
||||||
|
{{data.senderStreet}}<br>
|
||||||
|
{{data.senderPostCode}} {{data.senderCity}} {{(data.senderCountry) ? `(${data.senderCountry})` : null}}
|
||||||
|
</td>
|
||||||
|
<td id="cellHeader">
|
||||||
|
<span id="title">CMR</span><br>
|
||||||
|
{{data.cmrFk}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">2. Consignatario / Destinataire / Consignee</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.deliveryAddressFk}}<br>
|
||||||
|
{{data.deliveryName}}<br>
|
||||||
|
{{data.deliveryPhone || data.clientPhone}}
|
||||||
|
{{((data.deliveryPhone || data.clientPhone) && data.deliveryMobile) ? '/' : null}}
|
||||||
|
{{data.deliveryMobile}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">16. Transportista / Transporteur / Carrier</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.carrierName}}</b><br>
|
||||||
|
{{data.carrierStreet}}<br>
|
||||||
|
{{data.carrierPostalCode}} {{data.carrierCity}} {{(data.carrierCountry) ? `(${data.carrierCountry})` : null}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
3. Lugar y fecha de entrega /
|
||||||
|
Lieu et date de livraison /
|
||||||
|
Place and date of delivery
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.deliveryStreet}}<br>
|
||||||
|
{{data.deliveryPostalCode}} {{data.deliveryCity}} {{(data.deliveryCountry) ? `(${data.deliveryCountry})` : null}}<br>
|
||||||
|
{{(data.ead) ? formatDate(data.ead, '%d/%m/%Y') : null}}</b>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">17. Porteadores sucesivos / Transporteurs succesifs / Succesive Carriers</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
4. Lugar y fecha de carga /
|
||||||
|
Lieu et date del prise en charge de la merchandise /
|
||||||
|
Place and date of taking over the goods
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.loadStreet}}<br>
|
||||||
|
{{data.loadPostalCode}} {{data.loadCity}} {{(data.loadCountry) ? `(${data.loadCountry})` : null}}<br>
|
||||||
|
{{formatDate(data.created, '%d/%m/%Y')}}</b>
|
||||||
|
</td>
|
||||||
|
<td rowspan="2">
|
||||||
|
<span id="label">
|
||||||
|
18. Obervaciones del transportista /
|
||||||
|
Reserves et observations du transporteur /
|
||||||
|
Carrier's reservations and observations
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.truckPlate}}</b><br>
|
||||||
|
{{data.observations}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">5. Documentos anexos / Documents annexes / Documents attached</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="specialTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
7 & 8. Número de bultos y clase de embalage /
|
||||||
|
Number of packages and packaging class /
|
||||||
|
Nombre de colis et classe d'emballage
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div id="lineBreak">
|
||||||
|
<b>{{data.packagesList}}</b>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td id="itemCategoryList">
|
||||||
|
<table class="categoryTable">
|
||||||
|
<tr id="merchandiseLabels">
|
||||||
|
<td>6. Marcas y números / Brands and numbers / Marques et numéros</td>
|
||||||
|
<td>9. Naturaleza de la merc. / Nature of goods / Nature des marchandises</td>
|
||||||
|
<td>10. nº Estadístico / Statistical no. / n° statistique</td>
|
||||||
|
<td>11. Peso bruto / Gross weight / Poids brut (kg)</td>
|
||||||
|
<td>12. Volumen / Volume (m3)</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="merchandise in merchandises" id="merchandiseData">
|
||||||
|
<td>{{merchandise.ticketFk}}</td>
|
||||||
|
<td>{{merchandise.name}}</td>
|
||||||
|
<td>N/A</td>
|
||||||
|
<td>{{merchandise.weight}}</td>
|
||||||
|
<td>{{merchandise.volume}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div v-if="!merchandises" id="merchandiseDetail">
|
||||||
|
{{data.merchandiseDetail}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="mainTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
13. Instrucciones del remitente /
|
||||||
|
Instrunstions de l'expèditeur / Sender
|
||||||
|
instruccions
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.senderInstruccions}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
19. Estipulaciones particulares /
|
||||||
|
Conventions particulieres /
|
||||||
|
Special agreements
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.specialAgreements}}</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
14. Forma de pago /
|
||||||
|
Prescriptions d'affranchissement /
|
||||||
|
Instruction as to payment for carriage
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.paymentInstruccions}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">20. A pagar por / Être payé pour / To be paid by</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">21. Formalizado en / Etabile a / Estabilshed in</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.loadStreet}}</b><br>
|
||||||
|
{{data.loadPostalCode}} {{data.loadCity}} {{(data.loadCountry) ? `(${data.loadCountry})` : null}} <br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">15. Reembolso / Remboursement / Cash on delivery</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table class="signTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
22. Firma y sello del remitente /
|
||||||
|
Signature et timbre de l'expèditeur /
|
||||||
|
Signature and stamp of the sender
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="senderStamp"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
23. Firma y sello del transportista /
|
||||||
|
Signature et timbre du transporteur /
|
||||||
|
Signature and stamp of the carrier
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="deliveryStamp"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
24. Firma y sello del consignatario /
|
||||||
|
Signature et timbre du destinataire /
|
||||||
|
Signature and stamp of the consignee
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="signPath"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,45 @@
|
||||||
|
const config = require(`vn-print/core/config`);
|
||||||
|
const vnReport = require('../../../core/mixins/vn-report.js');
|
||||||
|
const md5 = require('md5');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
const prefixBase64 = 'data:image/png;base64,';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'cmr',
|
||||||
|
mixins: [vnReport],
|
||||||
|
async serverPrefetch() {
|
||||||
|
this.data = await this.findOneFromDef('data', [this.id]);
|
||||||
|
if (this.data.ticketFk) {
|
||||||
|
this.merchandises = await this.rawSqlFromDef('merchandise', [this.data.ticketFk]);
|
||||||
|
this.signature = await this.findOneFromDef('signature', [this.data.ticketFk]);
|
||||||
|
} else
|
||||||
|
this.merchandises = null;
|
||||||
|
|
||||||
|
this.senderStamp = (this.data.senderStamp)
|
||||||
|
? `${prefixBase64} ${this.data.senderStamp.toString('base64')}`
|
||||||
|
: null;
|
||||||
|
this.deliveryStamp = (this.data.deliveryStamp)
|
||||||
|
? `${prefixBase64} ${this.data.deliveryStamp.toString('base64')}`
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
description: 'The cmr id'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
signPath() {
|
||||||
|
if (!this.signature) return;
|
||||||
|
|
||||||
|
const signatureName = this.signature.signature
|
||||||
|
const hash = md5(signatureName.toString()).substring(0, 3);
|
||||||
|
const file = `${config.storage.root}/${hash}/${signatureName}.png`;
|
||||||
|
if (!fs.existsSync(file)) return null;
|
||||||
|
|
||||||
|
return `${prefixBase64} ${Buffer.from(fs.readFileSync(file), 'utf8').toString('base64')}`;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
reportName: cmr
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"format": "A4"
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
SELECT c.id cmrFk,
|
||||||
|
t.id ticketFk,
|
||||||
|
c.truckPlate,
|
||||||
|
c.observations,
|
||||||
|
c.senderInstruccions,
|
||||||
|
c.paymentInstruccions,
|
||||||
|
c.specialAgreements,
|
||||||
|
c.created,
|
||||||
|
c.packagesList,
|
||||||
|
c.merchandiseDetail,
|
||||||
|
c.ead,
|
||||||
|
s.name carrierName,
|
||||||
|
s.street carrierStreet,
|
||||||
|
s.postCode carrierPostCode,
|
||||||
|
s.city carrierCity,
|
||||||
|
cou.country carrierCountry,
|
||||||
|
s2.name senderName,
|
||||||
|
s2.street senderStreet,
|
||||||
|
s2.postCode senderPostCode,
|
||||||
|
s2.city senderCity,
|
||||||
|
cou2.country senderCountry,
|
||||||
|
a.street deliveryStreet,
|
||||||
|
a.id deliveryAddressFk,
|
||||||
|
a.postalCode deliveryPostalCode,
|
||||||
|
a.city deliveryCity,
|
||||||
|
a.nickname deliveryName,
|
||||||
|
a.phone deliveryPhone,
|
||||||
|
a.mobile deliveryMobile,
|
||||||
|
cou3.country deliveryCountry,
|
||||||
|
cl.phone clientPhone,
|
||||||
|
a2.street loadStreet,
|
||||||
|
a2.postalCode loadPostalCode,
|
||||||
|
a2.city loadCity,
|
||||||
|
cou4.country loadCountry,
|
||||||
|
co.stamp senderStamp,
|
||||||
|
s.stamp deliveryStamp
|
||||||
|
FROM cmr c
|
||||||
|
LEFT JOIN supplier s ON s.id = c.supplierFk
|
||||||
|
LEFT JOIN country cou ON cou.id = s.countryFk
|
||||||
|
LEFT JOIN company co ON co.id = c.companyFk
|
||||||
|
LEFT JOIN supplierAccount sa ON sa.id = co.supplierAccountFk
|
||||||
|
LEFT JOIN supplier s2 ON s2.id = sa.supplierFk
|
||||||
|
LEFT JOIN country cou2 ON cou2.id = s2.countryFk
|
||||||
|
LEFT JOIN `address` a ON a.id = c.addressToFk
|
||||||
|
LEFT JOIN province p ON p.id = a.provinceFk
|
||||||
|
LEFT JOIN country cou3 ON cou3.id = p.countryFk
|
||||||
|
LEFT JOIN client cl ON cl.id = a.clientFk
|
||||||
|
LEFT JOIN `address` a2 ON a2.id = c.addressFromFk
|
||||||
|
LEFT JOIN province p2 ON p2.id = a2.provinceFk
|
||||||
|
LEFT JOIN country cou4 ON cou4.id = p2.countryFk
|
||||||
|
LEFT JOIN ticket t ON t.cmrFk = c.id
|
||||||
|
WHERE c.id = ?
|
|
@ -0,0 +1,11 @@
|
||||||
|
SELECT s.ticketFk,
|
||||||
|
ic.name,
|
||||||
|
CAST(SUM(sv.weight) AS DECIMAL(10,2)) `weight`,
|
||||||
|
CAST(SUM(sv.volume) AS DECIMAL(10,3)) volume
|
||||||
|
FROM sale s
|
||||||
|
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN itemType it ON it.id = i.typeFk
|
||||||
|
JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||||
|
WHERE sv.ticketFk = ?
|
||||||
|
GROUP BY ic.id
|
|
@ -0,0 +1,5 @@
|
||||||
|
SELECT dc.id `signature`
|
||||||
|
FROM ticket t
|
||||||
|
JOIN ticketDms dt ON dt.ticketFk = t.id
|
||||||
|
LEFT JOIN dms dc ON dc.id = dt.dmsFk
|
||||||
|
WHERE t.id = ?
|
Loading…
Reference in New Issue