Zum Inhalt

DCC-Export

Export von Digitalen Kalibrierscheinen (Digital Calibration Certificates) im calServer v2.


Überblick

Der DCC-Export erzeugt XML-basierte Kalibrierscheine nach dem PTB-Standard (Physikalisch-Technische Bundesanstalt). Ein DCC enthält alle relevanten Informationen einer Kalibrierung in einem maschinenlesbaren Format.

PTB DCC-Standard

Das Digital Calibration Certificate (DCC) ist ein internationaler Standard für den digitalen Austausch von Kalibrierergebnissen. Er ermöglicht die automatische Verarbeitung und Validierung von Kalibrierdaten.

API-Endpunkt

Der DCC-Export ist über die V2-API verfügbar:

POST /api/v2/calibrations/{ctag}/dcc
Parameter Typ Beschreibung
ctag string Eindeutiger Kalibrierungs-Tag (CTAG)
report_id string Der angeklickte DCC-Eintrag (siehe Menükonfiguration). Ohne Angabe nimmt der Export den ersten konfigurierten Eintrag.
include_pdf bool Erzwingt XML-only, unabhängig von der Konfiguration. Vorgabe: true.

Antwort: JSON mit DCC-XML-Daten und Metainformationen.

{
    "data": {
        "type": "dcc",
        "attributes": {
            "xml": "<?xml version=\"1.0\" ...?>...",
            "calibration_id": "CTAG-123",
            "generated_at": "2026-03-08T10:00:00Z"
        }
    }
}

Berechtigung

Der Endpunkt erfordert die Berechtigung calibration_view sowie using_API.

Der DCC war in V2 ein fest verdrahteter Knopf: „DCC Export" stand im Mehr-Menü jeder Kalibrierung, für jeden, der die Kalibrierung sehen durfte, und welcher Kalibrierschein eingebettet wurde, riet der Server (erster Direktdruck-Bericht, sonst der erste überhaupt). Das ist bei einem Dokument, das ein Labor herausgibt, die falsche Voreinstellung: wer den Schein ausgeben darf und welcher Schein das ist, ist eine Laborentscheidung.

V1 hat das nie anders gemacht — dort hängt der Eintrag „Berichtsname (DCC XML)" am Report-Paket, das eine schema/dcc-mapping.json mitbringt (AdminReportSetting::hasDccMapping()), und erbt damit Platzierung, Reihenfolge und Rollen-/Gruppenkreis vom Bericht. V2 erzeugt das DCC aus den Kalibrierdaten, es gibt also keine Mapping-Datei zu erkennen — dieselbe Auskunft kommt jetzt aus der Konfiguration.

Admin → Berichte → Bericht bearbeiten (nur bei Grid calibration):

Feld Spalte Bedeutung
DCC-Export anbieten is_dcc Der Bericht bekommt zusätzlich den Eintrag „Berichtsname (DCC XML)".
Menschenlesbaren Schein einbetten dcc_include_pdf PDF im dcc:document. Aus = reines XML. Vorgabe: an.
Einzubettender Schein dcc_pdf_report_uID Welcher Kalibrierschein eingebettet wird. Leer = dieser Bericht selbst.

Was daraus folgt:

  • Wo — der DCC-Eintrag steht direkt unter seinem Bericht, im selben Kontext (menu: Detail, Master, Master-Detail) und in derselben Reihenfolge (sort_order). Ein Bericht mit menu = Master hat auf der Detailseite keinen DCC-Eintrag, weil er dort selbst keinen hat.
  • Durch wen — role_name/group_id des Berichts gelten unverändert (ReportAccessService). Wer den Bericht nicht sieht, bekommt weder ihn noch sein DCC — auch nicht über einen direkten Aufruf mit dessen report_id (404).
  • Welche Repräsentation — der eingebettete Schein ist wählbar. Mehrere Berichte dürfen is_dcc tragen; dann gibt es mehrere DCC-Einträge, jeder mit seinem eigenen Schein.
  • Der Feature-Flag enable_dcc_plugin bleibt der Hauptschalter. Ist er aus, liefert GET /calibrations/{ctag}/reports is_dcc: false und es erscheint kein Eintrag — genau wie in V1.

Die Regeln stehen in DccReportSettingResolver, die Eingabeprüfung in ValidatesDccReportConfig (DCC nur am Kalibrier-Grid, der eingebettete Schein muss ein aktiver Kalibrierbericht sein und darf nicht der Bericht selbst sein).

Bestandsinstallationen

Die Migration setzt is_dcc auf genau den Bericht, den der Export bisher schon eingebettet hätte — aber nur, wenn das DCC-Plugin eingeschaltet ist. Das bisherige Verhalten steht danach als Konfigurationszeile da, statt weiterhin geraten zu werden. Wer das Plugin erst später einschaltet, muss den Eintrag einmal setzen; ohne konfigurierten Bericht gibt es keinen Menüeintrag.

API-Clients

POST /calibrations/{ctag}/dcc ohne report_id funktioniert weiterhin, auch auf einer Installation ohne konfigurierten Eintrag — dort greift die alte automatische Auswahl des eingebetteten Scheins.

DCC-XML-Struktur

Ein generiertes DCC-XML enthält folgende Hauptbereiche:

<?xml version="1.0" encoding="UTF-8"?>
<dcc:digitalCalibrationCertificate
    xmlns:dcc="https://ptb.de/dcc">

    <!-- Administrative Daten -->
    <dcc:administrativeData>
        <dcc:calibrationLaboratory>...</dcc:calibrationLaboratory>
        <dcc:coreData>
            <dcc:uniqueIdentifier>CTAG-123</dcc:uniqueIdentifier>
            <dcc:beginPerformanceDate>2026-01-15</dcc:beginPerformanceDate>
            <dcc:endPerformanceDate>2026-01-15</dcc:endPerformanceDate>
        </dcc:coreData>
    </dcc:administrativeData>

    <!-- Messergebnisse -->
    <dcc:measurementResults>
        <dcc:measurementResult>...</dcc:measurementResult>
    </dcc:measurementResults>
</dcc:digitalCalibrationCertificate>

Inhaltsbereiche

Bereich Quelle Beschreibung
Kalibrierlabor Systemkonfiguration Name, Adresse, Akkreditierung
Kerndaten tbl_calibration CTAG, Datum, Prüfer
Prüfmittel tbl_inventory MTAG, Seriennummer, Beschreibung
Kunde tbl_customer Auftraggeber
Messergebnisse tbl_test_step Einzelne Prüfschritte mit Soll/Ist-Werten
Bezugsnormale tbl_standard_article Verwendete Referenznormale

Datenfluss

Calibration (CTAG)
    │
    ├── Inventory (MTAG) ──► Prüfmitteldaten
    │
    ├── Customer (KTAG) ──► Auftraggeberdaten
    │
    ├── TestSteps ──► Messergebnisse
    │       │
    │       └── Soll-/Ist-Werte, Toleranzen, Einheiten
    │
    └── StandardArticles ──► Bezugsnormale
            │
            └── Referenznormale mit Rückführbarkeit

Konfiguration

DCC-spezifische Einstellungen werden über die Property-Tabelle gesteuert:

Property Beschreibung
dcc_laboratory_name Name des Kalibrierlabors
dcc_accreditation_number Akkreditierungsnummer (DAkkS o.ä.)
dcc_schema_version DCC-Schema-Version (Standard: 3.2.1)

Validierung

Das generierte XML wird gegen das offizielle PTB-DCC-Schema validiert, bevor es an den Client zurückgegeben wird. Fehlende Pflichtfelder führen zu einer 422 Unprocessable Entity-Antwort mit Details zu den fehlenden Daten.

Keine erfundenen Werte

Ein Kalibrierschein, der behauptet, das Gerät habe 0 V angezeigt, wo in Wahrheit nichts erfasst wurde, ist schlimmer als gar kein Schein: das Dokument ist schemagültig, die Aussage ist falsch, und keine Prüfsoftware der Welt kann das erkennen. Deshalb gilt im Export durchgehend:

Fehlt eine Angabe, wird sie nicht ersetzt. Je nachdem, was das Schema zulässt, heißt das:

Fall Verhalten
Messwert oder Referenzwert einer Zeile fehlt Export wird abgelehnt (422), die betroffenen Zeilen stehen in der Meldung
Einheit einer Messzeile fehlt dito
Zeile ohne jeden Zahlenwert (Sichtprüfung, Bemerkung) keine Messzeile, blockiert nichts, kommt nicht in die Werteliste
Messabweichung oder Messunsicherheit fehlt in einer Zeile der Gruppe die ganze Größe entfällt; eine si:...XMLList hat keine Lücken
Land nicht zuordenbar dcc:countryCode entfällt (das Schema führt die Adressteile als Auswahl)
Kontakt ganz ohne Anschrift ausdrücklicher Vermerk in dcc:further statt eines geratenen Landes
Laborname nicht konfiguriert Export wird abgelehnt (422), mit Verweis auf Admin → Berichtsverwaltung → Berichtseinstellungen → Laborangaben

Die Vorabprüfung sitzt in DccXmlGeneratorService::missingPrerequisites() und läuft im Endpunkt, bevor überhaupt XML entsteht.

Die Ablehnung als Arbeitsliste

Der Satz in error.detail sagt, was fehlt, aber nicht, wo es steht. „Zeile 3 (Resistance 100 Ohm): angezeigter Messwert fehlt" ist als Fließtext richtig und als Arbeitsanweisung nutzlos — gesucht werden muss trotzdem selbst. Die 422 trägt deshalb dieselbe Auskunft maschinenlesbar daneben:

{
  "error": {
    "status": 422,
    "title": "DCC Export Incomplete",
    "detail": "Für ein gültiges DCC (PTB 3.3.0) fehlt noch: …",
    "missing": [
      {
        "code": "lab_name",
        "label": "der Name des Kalibrierlabors",
        "fix": "report_variable",
        "hint": "Admin → Berichtsverwaltung → Berichtseinstellungen → Laborangaben",
        "rows": []
      },
      {
        "code": "results",
        "label": "ein vollständiges Wertepaar aus Referenz- und Messwert in 1 Messzeile(n)",
        "fix": "results",
        "hint": "Zeile 3 (Resistance 100 Ohm): angezeigter Messwert fehlt",
        "rows": [
          {
            "row_number": 3,
            "label": "Resistance 100 Ohm",
            "reason": "angezeigter Messwert fehlt"
          }
        ]
      }
    ]
  }
}
Feld Bedeutung
code Stabiler Bezeichner: inventory, customer, calibration_date, lab_name, results
label Klartext, wie er auch im Satz steht
fix Sprungziel: calibration_record (Datensatz bearbeiten), report_variable (Berichtsverwaltung), results (Messwertraster)
hint Ergänzung — Pfad zur Einstellung bzw. Kurzliste der Zeilen (max. 5, dann …)
rows Betroffene Messzeilen, vollständig und nach row_number sortiert

row_number ist die Nummer aus dem Messwertraster (results.row_num), nicht die Position in der Liste — nur damit findet man die Zeile wieder, wenn die Erfassung nicht bei 1 anfängt.

Satz und Liste stammen aus derselben Quelle: missingPrerequisites() rendert seine Sätze aus genau diesen Einträgen (describeMissing()), sie können also nicht auseinanderlaufen. Clients, die nur detail lesen, bleiben gültig; das Frontend zeigt die Ablehnung als stehenden Block (DccIssuePanel) mit einem Link je Punkt statt als Toast, der nach fünf Sekunden verschwindet, während man noch sucht. Ein Klick auf eine Messzeile öffnet den Ergebnis-Tab und hebt genau diese Zeile hervor.

Verhaltensänderung

Installationen ohne gepflegten lab_name bekommen jetzt eine 422 statt eines Scheins mit dem Platzhalter „Kalibrierlabor" als Aussteller. Einmalige Konfiguration, danach ist Ruhe.

refTypes

refType ist die maschinenlesbare Sprungmarke im DCC — daran findet ein Empfänger den Messwert, das Normal, die Messabweichung. Die XSD prüft sie nicht (refTypesType ist eine Liste beliebiger Zeichenketten); verbindlich ist der refType-Thesaurus des DKD (digilab.ptb.de/dkd/refType/vocab/). Ein erfundener Bezeichner ist deshalb kein Schemafehler, sondern totes Gewicht.

Der Export schreibt Marken aus dem basic_-Namensraum (messgrößen- übergreifend). DccRefTypeTest::DKD_REF_TYPES führt die vollständige Liste mit Herkunft und Platzierungsregel und schlägt fehl, sobald eine neue dazukommt.

Jede Marke ist gegen den Thesaurus geprüft (Stand 2026-09-03). Ersetzt wurden dabei basic_recalibration → basic_recalibrationDate, basic_uncertainty → basic_methodMeasurementUncertainty und gp_method → basic_calibrationMethod; einen Namensraum gp_ führt der Thesaurus nicht.

Eigene Marken

Für zwei Aussagen führt der Thesaurus keinen Begriff: die Messbedingungen als Freitext und die angewandte Prüfvorschrift. Beide tragen einen eigenen Namensraum und werden im Schein selbst erklärt:

refType Element Bedeutung
calServer_measurementConditions dcc:usedMethod Messbedingungen als Freitext (Report-Variable)
calServer_measuringProcedure dcc:usedMethod angewandte Prüfvorschrift samt Version

dcc:administrativeData/dcc:refTypeDefinitions erklärt jede davon mit Namensraum und Link. Der Knoten steht nur im Schein, wenn er eine solche Marke auch trägt — refTypeDefinitionListType verlangt mindestens einen Eintrag.

Ein erfundenes basic_… sähe amtlich aus und wäre keins; eine erklärte eigene Marke kann ein Empfänger auflösen.

Bewusst unverändert

basic_pressure sitzt an der dcc:influenceCondition, obwohl der Thesaurus dafür nur ruleQuantity führt (basic_temperature und basic_humidityRelative daneben auch ruleInfluenceCondition). Welches Element eine Marke tragen darf, wertet bisher kein Prüfer aus; eine der drei Bedingungen anders zu schreiben brächte nur Uneinheitlichkeit.

dcc:result trägt bewusst keine Marke: der früher dort stehende Bezeichner gp_measuredQuantity stand in keinem Namensraum, und die Bedeutung steckt ohnehin an den dcc:quantity-Elementen darin (basic_referenceValue, basic_measuredValue, basic_measurementError).

Einheiten: D-SI-Notation

Das DCC schreibt Einheiten nicht als mV oder °C, sondern in der D-SI-Notation der PTB: \milli\volt, \degreecelsius. Die Umsetzung macht DccXmlGeneratorService::toSiUnit() in dieser Reihenfolge:

  1. Schreibvarianten — Ohm, kOhm, uV, degC werden auf das kanonische Symbol gezogen (Ω, kΩ, µV, °C). Bestandsdaten sind voll davon: der Stammdatenkatalog führt Ω, getippt wurde Ohm.
  2. Zusammengesetzte und festgeschriebene Symbole — Produkte (Nm → \newton\metre), Quotienten (m/s → \metre\per\second), Exponenten (m³ → \metre\tothe{3}) und die Fälle mit fester Schreibweise (°C → \degreecelsius, dB → \decibel).
  3. Stammdatenkatalog — physical_quantity.dsi_unit, gepflegt unter Admin → Anforderungen → Physikalische Größen (siehe unten).
  4. Basiseinheit ohne Vorsatz — V → \volt, h → \hour.
  5. Vorsatz + Basiseinheit — jede Kombination aus der si_prefix-Tabelle und dem Einheitensymbol: mV, kΩ, hPa, daN, µS. Der Vorsatz greift auch auf Katalogeinträge, sofern deren Schreibweise eine einzelne Marke ist.
  6. Sonst der |-Auslassungsstrich — HRC wird zu |HRC.

Punkt 6 ist der Unterschied zu früher: eine Einheit außerhalb der Liste ging unverändert ins XML und war dort keine gültige D-SI-Zeichenkette. Der Auslassungsstrich ist die vom D-SI dafür vorgesehene Schreibweise — der Wert bleibt lesbar, das Dokument bleibt gültig. Maschinell auswertbar ist eine solche Einheit für die Gegenstelle trotzdem nicht; der Prozedureditor weist beim Erfassen darauf hin.

Drei Regeln des D-SI, die im Code sichtbar sind:

  • R10 — Minute, Stunde, Tag, Hektar, mmHg und die Verhältnisgrößen (%, ppm) vertragen keinen Vorsatz. kmin gibt es nicht und wird |kmin.
  • R11 — das Kilogramm ist die Basiseinheit und heißt \kilogram, nicht \kilo\gram.
  • R12 — Dezibel heißt \decibel, nicht \deci\bel.

Einheiten ohne D-SI-Entsprechung sind bewusst als |-Einheit hinterlegt: pH (zerfiele sonst in Piko + Henry), dBm (ein Pegel bezogen auf 1 mW, kein Produkt aus Dezibel und Milliwatt) sowie die Härteskalen HRC, HV, HBW.

Die XSD prüft die Einheit nicht

si:unitType ist in D-SI 2.2.1 ein unbeschränkter xs:string — eine Schemavalidierung nimmt auch HRC an. Die Grammatik steht in den D-SI-Regeln. DccUnitNotationTest prüft deshalb jede erzeugbare Zeichenkette gegen die Regex der PTB-Referenzimplementierung (tests/Fixtures/dcc-schema/d-si-unit.regex).

Einheiten selbst richtigstellen

Die Zuordnung Symbol → D-SI stand früher ausschließlich als Konstante im Generator. Alles, was die dortigen Tabellen nicht kannten, ging als |Einheit in den Schein — gültig, aber maschinell wertlos, und ohne Release nicht zu korrigieren. Dabei kommt die Einheit aus einem Katalog, den das Labor selbst pflegt.

Deshalb hat physical_quantity die Spalte dsi_unit, editierbar unter Admin → Anforderungen → Physikalische Größen:

  • Leer: der Export leitet die Schreibweise wie bisher aus dem Symbol ab.
  • Gesetzt: der Katalogwert gewinnt — auch gegen die eingebaute Tabelle.
  • Geprüft wird die Form (\marke, klein, ohne Leerzeichen, Exponent als \tothe{2}, | für Einheiten außerhalb des D-SI), nicht das Vokabular: eine exotische, aber gültige Einheit soll nicht an der Eingabemaske scheitern.

Die Werksvorgabe bringt die Schreibweise für alle rund 50 Katalogeinträge mit (App\Support\SiUnitCatalog); die Migration trägt sie für Bestandszeilen nach, ohne selbst gesetzte Werte anzufassen. Die Spalte ist eine reine V2-Änderung — V2 teilt sich keine Tabellen mehr mit V1 und läuft auch nach dessen Abschaltung eigenständig weiter.

Verwandte Dokumentation