Rapporten för ett ifyllt protokoll kan skrivas ut med en egen mall i stället för standardrapporten. Mallen kan vara en Word-fil (.docx) eller en HTML-fil. Den här artikeln beskriver hur du bygger en HTML-mall. Principen är densamma som för offertmallar (se artikeln Offertmallar), men protokollmallar har egna taggar och dessutom tillgång till all protokolldata via JavaScript, vilket gör att du kan bygga upp listan med checkpunkter helt fritt.
Koppla en mall till ett protokoll
- Gå till Inställningar > Protokoll och öppna det protokoll som ska få en egen rapport.
- Öppna Fler uppgifter.
- Ladda upp din HTML-fil i fältet Mall.
- Välj en arbetsorder där protokollet är ifyllt i rutan under mallen och klicka på Förhandsgranska html eller Förhandsgranska resultat (PDF) för att se hur mallen ser ut med riktiga värden.
När protokollet därefter skrivs ut eller skickas som dokumentation från en arbetsorder används mallen automatiskt i stället för standardrapporten. Vill du gå tillbaka till standardrapporten tar du bara bort mallen från protokollet.
Mallen får vara högst 800 kB. Den vanligaste orsaken till att en fil blir för stor är inbäddade bilder. Använd taggen för logotyp (se nedan) i stället för att bädda in logotypen i filen.
Så fungerar mallen
Mallen är en vanlig HTML-sida med egen CSS som renderas som en webbsida och skrivs ut till PDF i A4-format. Du kan därför använda CSS för sidstorlek, marginaler, sidhuvud och sidfot. I mallen skriver du taggar i formatet {{ tagg }}. Varje tagg byts ut mot motsvarande värde från protokollet, arbetsordern, anläggningen, aggregatet och filialen när rapporten skapas. Taggar som saknar värde byts ut mot en tom sträng.
Taggarna är inte skiftlägeskänsliga, så {{ workOrder.number }} och {{ WorkOrder.Number }} ger samma resultat. Med punkt går det att gå vidare in i underliggande objekt, till exempel {{ workOrder.deliveryAddress.city }}.
Taggar
Här är de vanligaste taggarna. Datum formateras efter språket hos den användare som skapar rapporten.
Protokollet
- {{ protocol.title }} – protokollets namn.
- {{ protocol.number }} – protokollets nummer.
- {{ protocol.internalReferenceNumber }} – intern referens.
- {{ protocol.info }} och {{ protocol.description }} – information och beskrivning från protokollinställningarna.
- {{ number }} – det ifyllda protokollets löpnummer.
- {{ date }} – datum då protokollet fylldes i.
- {{ notes }} – anteckning på det ifyllda protokollet.
- {{ progress }} – status på protokollet.
- {{ operatingTime }} – drifttid.
- {{ signatureName }} – namnet på den som signerat protokollet.
Checkpunkter via numrering
Enskilda checkpunkter nås med {{ item.N.egenskap }}, där N är punktens ordningsnummer i protokollet (rubriker räknas också). Det passar bra när protokollet har ett fast antal punkter och varje värde ska stå på en bestämd plats i rapporten.
- {{ item.1.title }} – punktens rubrik.
- {{ item.1.value }} – ifyllt värde. Vid flera kolumner finns även value2, value3 och så vidare.
- {{ item.1.unit }} – punktens enhet.
- {{ item.1.assessment }} – bedömningen som text, till exempel Klar utan anmärkning. Egna bedömningstexter från protokollet används om de är ifyllda.
- {{ item.1.notes }} – anteckning på punkten.
Arbetsorder
- {{ workOrder.number }} – ordernummer.
- {{ workOrder.description }} – orderns beskrivning.
- {{ workOrder.externalNumber }} – externt nummer.
- {{ workOrder.start }} och {{ workOrder.finish }} – planerad start och slut.
- {{ workOrder.deliveryAddress.street }}, {{ workOrder.deliveryAddress.postalCode }}, {{ workOrder.deliveryAddress.city }} – leveransadress.
- {{ workOrder.invoiceCustomer.name }} – fakturakundens namn.
- {{ workOrder.contact.name }} – kontaktperson på ordern.
- {{ workOrder.buyerName }} – beställare.
- {{ workOrder.mainWorker.name }} – ansvarig tekniker på ordern.
- {{ workOrder.ourReference.name }} – vår referens.
- {{ workOrder.project.name }} – projekt.
Anläggning
- {{ facility.name }} och {{ facility.number }} – anläggningens namn och nummer.
- {{ facility.propertyDesignation }} – fastighetsbeteckning.
- {{ facility.customer.name }} – anläggningens kund.
- {{ facility.mainContact.name }} – huvudkontakt.
- {{ facility.phoneNumber }} – telefon.
Aggregat
Aggregattaggarna har värden när protokollet gäller aggregat och ett aggregat är valt på ordern.
- {{ workObject.name }} och {{ workObject.number }} – aggregatets namn och nummer.
- {{ workObject.serialNumber }} – serienummer.
- {{ workObject.manufactureName }} – fabrikat.
- {{ workObject.typeName }} – typ.
- {{ workObject.placement }} – placering.
- {{ workObject.provides }} – betjänar.
- {{ workObject.installationDate }} – installationsdatum.
- {{ workObject.note }} – anteckning.
- {{ workObject.fGasProperties.refrigerantArticle.refrigerant.name }} – köldmedium (kylaggregat).
- {{ workObject.fGasProperties.amount }} – köldmediemängd i kg (kylaggregat).
- {{ workObject.totalOperatingTimePerWeek }} – drifttid per vecka (ventilationsaggregat).
Tekniker och filial
- {{ mainWorker.name }}, {{ mainWorker.email }}, {{ mainWorker.cellPhonenumber }} – tekniker som fyllt i protokollet.
- {{ mainWorker.signature }} – teknikerns signatur som bild. Används som src på en img-tagg.
- {{ branch.name }} – filialens namn.
- {{ branch.address.street }}, {{ branch.address.postalCode }}, {{ branch.address.city }} – filialens adress.
- {{ branch.phonenumber }}, {{ branch.email }}, {{ branch.website }} – kontaktuppgifter.
- {{ branch.orgNumber }} – organisationsnummer.
- {{ branch.logo }} – filialens logotyp som bild. Används som src på en img-tagg.
Filter
Numeriska värden kan formateras med tusentalsavgränsare genom att lägga till ett filter efter taggen: {{ workOrder.price | thousands_separator }}.
Bygga checkpunktslistan med JavaScript
De flesta protokoll har olika antal punkter, och ibland ska punkter döljas eller visas med ikoner beroende på bedömning. Då räcker inte de fasta taggarna. I stället kan du hämta hela protokollet som JSON med den särskilda taggen {{ . }} och bygga upp tabellen i ett script i mallen:
const json = JSON.parse({{ . }});
Taggen byts ut mot en färdig JSON-sträng, så raden ovan ger dig ett objekt med all data. Observera att egenskaperna i JSON-objektet börjar med stor bokstav, till exempel json.Protocol och json.WorkOrder. De viktigaste delarna är:
- json.Protocol.Items – listan med protokollets punkter i ordning. Varje punkt har bland annat Id, Title, Type, Unit, ShowValue, ShowNotes och ValuesCount.
- json.Values – de ifyllda värdena, uppslagna på punktens Id: json.Values[item.Id]. Varje värde har Assessment, Value, Value2 till Value6 samt Notes.
- json.Protocol.HideNotApplicableProtocolItems – inställningen Dölj ej tillämpliga på protokollet.
- json.WorkOrder, json.Facility, json.WorkObject, json.Branch, json.MainWorker och json.Date – samma data som taggarna ovan.
Typ av punkt
Fältet Type på en punkt anger vilken typ punkten har:
- 0 – Bedömning
- 1 – Ja/Nej
- 2 – Rubrik
- 3 – Länkad
- 4 – Entitet
- 5 – Ja/Nej/Ej tillämplig
Bedömning
Fältet Assessment på ett ifyllt värde är en siffra:
- -1 – Ej kontrollerad
- 0 – Klar utan anmärkning
- 1 – Bör åtgärdas
- 2 – Skall åtgärdas
- 3 – Åtgärdat
- 4 – Ej tillämplig
Exempel på script
Scriptet nedan fyller en tabell med id rows med en rad per punkt. Rubriker får en egen rad, bedömningen visas som en grön bock eller ett rött kryss, och anteckningar läggs på en egen rad under punkten. Punkter som markerats som ej tillämpliga döljs om protokollet har inställningen Dölj ej tillämpliga.
<table class="checklist"><tbody id="rows"></tbody></table>
<script>
const json = JSON.parse({{ . }});
const rows = document.getElementById("rows");
for (const item of json.Protocol.Items) {
if (!item.Title) {
continue;
}
const value = json.Values[item.Id];
const isHeading = item.Type === 2;
if (value && value.Assessment === 4 && json.Protocol.HideNotApplicableProtocolItems) {
continue;
}
const row = rows.appendChild(document.createElement("tr"));
row.className = isHeading ? "heading" : "row";
if (isHeading) {
const cell = row.appendChild(document.createElement("td"));
cell.colSpan = 3;
cell.textContent = item.Title;
continue;
}
const assessment = row.appendChild(document.createElement("td"));
assessment.className = "assessment";
switch (value ? value.Assessment : -1) {
case 0:
case 3:
assessment.innerHTML = '<span class="ok">✓</span>';
break;
case 1:
case 2:
assessment.innerHTML = '<span class="nok">✗</span>';
break;
default:
assessment.innerHTML = '<span class="neutral">–</span>';
}
const title = row.appendChild(document.createElement("td"));
title.textContent = item.Title;
const valueCell = row.appendChild(document.createElement("td"));
if (value && value.Value) {
valueCell.textContent = value.Value + " " + item.Unit;
}
if (value && value.Notes) {
const noteRow = rows.appendChild(document.createElement("tr"));
const note = noteRow.appendChild(document.createElement("td"));
note.colSpan = 3;
note.className = "note";
note.textContent = "Anteckning: " + value.Notes;
}
}
</script>
Flera kolumner
Om en punkt har flera kolumner (inställningen Antal kolumner på punkten) anger item.ValuesCount hur många kolumner den har. Det första värdet ligger i value.Value och de följande i value.Value2, value.Value3 och så vidare. Kolumnrubrikerna hämtas från rubrikpunkten ovanför, där de ligger i item.Value1 till item.Value5. Vill du visa kolumnerna lägger du till en cell per kolumn i scriptet i stället för den enda värdecellen i exemplet.
Grundstruktur för mallen
Ett bra sätt att få sidhuvud och sidfot som upprepas på varje sida är att lägga innehållet i en tabell där sidhuvudet ligger i thead och en tom cell med samma höjd som sidfoten ligger i tfoot. Sidfoten placeras med position fixed längst ner på sidan. Nedan är en komplett men enkel mall som du kan utgå ifrån. Byt ut färger, typsnitt och texter så att den passar er profil.
<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8" />
<style>
@page { size: A4; margin: 0; }
html, body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
font-size: 10pt;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
#page-footer { position: fixed; bottom: 0; height: 15mm; font-size: 8pt; }
.page, #page-header, #page-footer {
padding-left: 20mm;
padding-right: 20mm;
width: calc(210mm - 40mm);
}
h1 { font-size: 14pt; margin: 0; }
.header-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 8mm; }
.field span { display: block; }
.field .label { font-weight: bold; }
table.checklist { width: 100%; border-collapse: collapse; }
table.checklist td { border: 1px solid #ddd; padding: 4px 6px; vertical-align: top; }
tr.heading td { font-weight: bold; background: #f2f2f2; }
td.assessment { width: 24px; text-align: center; }
td.note { font-style: italic; }
.ok { color: #2e7d32; }
.nok { color: #c62828; }
.neutral { color: #757575; }
.signature img { max-height: 25mm; }
</style>
</head>
<body>
<div id="page-footer">
<table width="100%">
<tr>
<td>{{ branch.name }}<br />{{ branch.address.street }}, {{ branch.address.postalCode }} {{ branch.address.city }}</td>
<td style="text-align: right">{{ branch.phonenumber }}<br />{{ branch.website }}</td>
</tr>
</table>
</div>
<table width="100%" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td>
<div id="page-header">
<table width="100%">
<tr>
<td><h1>{{ protocol.title }}</h1></td>
<td style="text-align: right"><img width="200px" src="{{ branch.logo }}" /></td>
</tr>
</table>
</div>
</td>
</tr>
</thead>
<tfoot>
<tr><td><div style="height: 20mm"></div></td></tr>
</tfoot>
<tbody>
<tr>
<td>
<div class="page">
<div class="header-grid">
<div class="field"><span class="label">Kund</span><span>{{ workOrder.invoiceCustomer.name }}</span></div>
<div class="field"><span class="label">Anläggning</span><span>{{ facility.name }}</span></div>
<div class="field"><span class="label">Arbetsorder</span><span>{{ workOrder.number }}</span></div>
<div class="field"><span class="label">Datum</span><span>{{ date }}</span></div>
<div class="field"><span class="label">Tekniker</span><span>{{ mainWorker.name }}</span></div>
<div class="field"><span class="label">Aggregat</span><span>{{ workObject.name }}</span></div>
<div class="field"><span class="label">Fabrikat</span><span>{{ workObject.manufactureName }}</span></div>
<div class="field"><span class="label">Placering</span><span>{{ workObject.placement }}</span></div>
</div>
<table class="checklist"><tbody id="rows"></tbody></table>
<p>{{ notes }}</p>
<div class="signature">
<img src="{{ mainWorker.signature }}" />
<p>{{ mainWorker.name }}</p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<script>
// Scriptet från avsnittet "Exempel på script" ovan
</script>
</body>
</html>
Tips
- Använd Förhandsgranska html under arbetet. Där kan du öppna webbläsarens utvecklarverktyg, se hela JSON-objektet från {{ . }} och felsöka scriptet. Använd Förhandsgranska resultat för att kontrollera sidbrytningar och sidfot i den färdiga PDF:en.
- Sätt print-color-adjust till exact i CSS:en, annars kan bakgrundsfärger falla bort i PDF:en.
- Punkter utan rubrik och punkter som är dolda visas ändå i json.Protocol.Items, så filtrera bort dem i scriptet.
- Hämta logotyp och signatur via taggarna i stället för att bädda in bilder i filen, så håller sig mallen liten.
- Testa mallen på en arbetsorder som har både anläggning och aggregat, så att alla taggar får värden.
Kommentarer
0 kommentarer
logga in för att lämna en kommentar.