Zum Inhalt

Begleitschein je Rolle

Diese Seite verfolgt einen Begleitschein durch alle Beteiligten und zeigt für jede Rolle, was ihr System tut: abholen, den eigenen Layer anlegen, Angaben eintragen, signieren lassen, speichern und weitergeben. Warum die Schritte so aufeinander folgen, erklärt Verfahren und Rollen. Die einzelnen Aufrufe mit allen Parametern und ihre REST-Form stehen unter Begleitschein und Übernahmeschein.

Die Beispiele sind C# gegen Zedal.Sdk. Jede Rolle steht für sich: Ein Beförderer-System braucht nur die gemeinsamen Bausteine und den Abschnitt Beförderer.

Gemeinsame Bausteine

Setup

using System;
using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;      // Timestamp
using Infotech.Zedal.Bxa.Bmu.V1.Api;       // XmlOptions
using Infotech.Zedal.Core.V1.Api;          // DocumentId, FetchDocumentNumbersRequest
using DateTime = System.DateTime;          // Infotech.Zedal.Core.V1.Api has its own DateTime
using Infotech.Zedal.Documents.V1;         // Document, StringField, DoubleField, BoolField, TimestampField, FieldMeta
using Infotech.Zedal.Documents.V1.Bmu;     // BGSDokument, UNSDokument, Nummer, Beteiligter, Adresse, Befoerderer, Entsorger, LayerType, Rolle
using Zedal.Sdk;
using Zedal.Sdk.DocumentHandler;           // BmuDocumentHandler

Der angemeldete IZedalClient heißt in allen Beispielen client (Erste Schritte).

Feldwerte

UpdateDocument schreibt nur Werte mit Meta.InLayer = true und ignoriert alle anderen, ohne Fehler (Felder schreiben). Ein paar kleine Helfer halten die Beispiele lesbar:

static FieldMeta InLayer() => new FieldMeta { InLayer = true };

static StringField Text(string value) => new StringField { Value = value, Meta = InLayer() };
static DoubleField Amount(double value) => new DoubleField { Value = value, Meta = InLayer() };
static BoolField Flag(bool value) => new BoolField { Value = value, Meta = InLayer() };

// Datumsfelder sind Zeitstempel. Für einen Kalendertag übergibst du Mitternacht UTC dieses Tages.
static TimestampField Date(DateTime day) => new TimestampField
{
    Value = Timestamp.FromDateTime(DateTime.SpecifyKind(day.Date, DateTimeKind.Utc)),
    Meta = InLayer()
};

// Nummer ohne Prüfziffer; die Prüfziffer berechnet der Dienst (XmlOptions.Pruefziffer = Calculate).
static Nummer Number(string number) => new Nummer { Nr = Text(number) };

// Ein Beteiligter mit behördlicher Nummer, Name und Anschrift aus deinen Stammdaten.
static Beteiligter Party(MasterData m) => new Beteiligter
{
    BehoerdlicheNr = Number(m.Number),
    Adresse = new Adresse
    {
        Name1 = Text(m.Name),
        Strasse1 = Text(m.Street),
        Hausnummer = Text(m.HouseNumber),
        Plz = Text(m.PostalCode),
        Ort1 = Text(m.City),
        Staat = Text(m.Country)          // zwei Großbuchstaben, etwa "DE"
    }
};

MasterData, Transport und die übrigen Eingabetypen der Beispiele stehen für die Daten aus deinem System. Protobuf-Felder nehmen kein null an: Lass eine Angabe, die du nicht hast, ganz weg, statt Text(null) zu übergeben.

Eingänge abholen

Ein Begleitschein, den ein anderer Beteiligter speichert und vermittelt, kommt auf einem von zwei Wegen bei dir an:

Absender Wo der Begleitschein ankommt Abholen mit
Beteiligter bei ZEDAL Versandpapier-Bereich, in einer Warteschlange PeekNextTransportDocument, SetTransportDocumentReceived
Beteiligter bei einem anderen eANV-Anbieter, über die ZKS-Abfall Inbox Inbox-Importer (ImportTransportDocumentFromInbox)

Dein System fragt deshalb beide Wege ab. Die Warteschlange des Versandpapier-Bereichs rückt erst nach deiner Quittung weiter:

// Holt das nächste zugestellte Transportdokument und lädt es als BMU-Dokument.
// Liefert null, wenn nichts ansteht.
async Task<BmuDocumentHandler> NextIncoming()
{
    var (docId, logId) = await client.CoreApi.Documents.PeekNextTransportDocument();
    if (string.IsNullOrEmpty(docId?.Id))
        return null;

    byte[] xml = await client.CoreApi.Documents.ExportCurrentDocument(docId, logId);
    var bmu = client.DocumentHandler.CreateBmuHandler();
    await bmu.Load(xml);

    await SaveInOwnSystem(bmu.DocXml);   // erst sichern ...
    await client.CoreApi.Documents.SetTransportDocumentReceived(docId, logId);   // ... dann quittieren
    return bmu;
}

Den Posteingang übernimmt der Inbox-Importer des SDK in den Versandpapier-Bereich. Danach lädst du die übernommenen Dokumente wie oben:

var importer = client.DocumentHandler.CreateInboxImporter();
var report = await importer.ImportAsTransportAsync(new[] { "BGS", "UNS" });

foreach (var entry in report.Imported)
{
    byte[] xml = await client.CoreApi.Documents.ExportCurrentDocument(entry.LocalDocId);
    // laden und sichern wie in NextIncoming
}
// report.Failed: Einträge, die nicht übernommen werden konnten; sie bleiben im Posteingang

Vier Regeln für das Abholen:

  • Erst sichern, dann quittieren. Bis zur Quittung liefert PeekNextTransportDocument immer wieder denselben Eintrag. Bricht deine Verarbeitung dazwischen ab, geht nichts verloren. Ein Eintrag, den du nie quittierst, hält die Warteschlange aber an.
  • In der Warteschlange stehen alle Transportdokumente, nicht nur Begleitscheine. Prüf den Dokumenttyp, bevor du einen Eintrag als BMU-Dokument behandelst (bmu.Document.DocumentsCase).
  • Jede neue Version kommt erneut an. ZEDAL vermittelt bei jedem Speichern, also bekommst du denselben Begleitschein nach jedem weiteren Layer wieder zugestellt. Ordne Eingänge über die Begleitscheinnummer zu und entscheide am Stand der Layer, ob für dich etwas zu tun ist.
  • Die Übernahme aus dem Posteingang gibt es nur über gRPC und das SDK, nicht über REST (Grenzen des REST-Wegs).

Einen Push-Kanal gibt es derzeit nicht; wie oft du abfragst, steht unter Webhooks und Event-Benachrichtigung.

Ist meine Rolle dran?

Die Layer stehen mit Signaturen in Document.Meta.Layers. Bevor du deinen Layer anhängst, prüfst du, ob dein Vorgänger signiert hat:

static bool IsSigned(BmuDocumentHandler bmu, string layerType) =>
    bmu.Document.Meta.Layers.Any(l => l.Type == layerType && l.Signatures.Count > 0);

Die Typnamen sind die des BMU-Schemas: BGSVorlageLayer, BGSERZLayer, BGSBEFLayer, BGSZWLLayer, BGSENTLayer, BGSErgaenzungsLayer und BGSBEHLayer.

Die API erzwingt die Reihenfolge der Signaturen nicht

AddLayer hängt einen Layer auch an, wenn der vorherige noch nicht signiert ist. Dass jeder Beteiligte erst nach der Signatur seines Vorgängers übernimmt, muss dein System sicherstellen, etwa mit IsSigned wie oben.

Signieren

Jede Rolle lässt ihren Layer von einem Menschen qualifiziert signieren. In den Beispielen steht dafür ein Aufruf:

// Lässt den obersten Layer von einem Menschen signieren und lädt das signierte XML in den Handler.
Task Sign(BmuDocumentHandler bmu, Rolle role);

Wie du ihn mit dem WebSigner umsetzt und was dort heute schon geht, steht im Abschnitt Signieren am Ende dieser Seite.

Nummern und Muster

Jeder Begleitschein und jeder Übernahmeschein braucht eine Nummer aus dem System der Länder. ZEDAL ruft sie zentral ab und gibt sie über FetchNumbers aus:

string bgsNr = (await client.CoreApi.Common.FetchNumbers(1,
    FetchDocumentNumbersRequest.Types.NumberType.BmuBgs)).Single();
// 14 Zeichen: „1“ und 13 Ziffern, ohne Prüfziffer

Nummern nur abrufen, wenn du sie brauchst

ZEDAL vergibt jede abgerufene Nummer fest an deinen Account; ein zweiter Abruf liefert andere Nummern. Liegen bei dir zu viele abgerufene, noch nicht verwendete Nummern aus den letzten 14 Tagen, in der Regel 100, lehnt ZEDAL weitere Abrufe ab. Ruf Nummern deshalb erst ab, wenn du den Begleitschein anlegst, und halte keinen großen Vorrat. Ist der Vorrat bei ZEDAL knapp, liefert ein Abruf auch weniger Nummern als angefordert, ohne Fehler; prüf die Anzahl.

FetchNumbers gibt es nur über gRPC und das SDK, nicht über REST.

Statt aus Daten kann ein Begleitschein aus einem ZEDAL-Muster entstehen, das in deinem Account hinterlegt ist. Wie du Muster auflistest und lädt, steht unter Aus einem ZEDAL-Muster. Danach geht es weiter wie beim Erzeuger ab Schritt 2.

Erzeuger

Der Erzeuger legt den Begleitschein an, trägt die Angaben zum Transport ein und signiert spätestens bei der Übergabe an den Beförderer.

async Task<DocumentId> CreateConsignmentNote(Transport t)
{
    var bmu = client.DocumentHandler.CreateBmuHandler();
    string bgsNr = (await client.CoreApi.Common.FetchNumbers(1,
        FetchDocumentNumbersRequest.Types.NumberType.BmuBgs)).Single();

    // 1. Vorlage: alles, was schon vor der Abholung feststeht, mit allen Beteiligten
    var bgs = new BGSDokument
    {
        BgsNr = Number(bgsNr),
        NachweisNr = Number(t.CertificateNumber),
        Abfallschluessel = Text(t.WasteCode),              // sechs Ziffern, ohne Leerzeichen
        Abfallbezeichnung = Text(t.WasteDescription),
        Erzeuger = Party(t.Producer),
        Befoerderer1 = new Befoerderer { Beteiligter = Party(t.Carrier) },
        Entsorger = new Entsorger { Beteiligter = Party(t.Disposer) }
    };
    if (t.InterimStorage != null)
        bgs.Zwischenlager = new Zwischenlager { Beteiligter = Party(t.InterimStorage) };

    await bmu.Create(new Document { Bgs = bgs }, vorlageLayer: true, new XmlOptions
    {
        AtbRolle = Rolle.Erz,
        Pruefziffer = XmlOptions.Types.Pruefziffer.Calculate,
        Validation = XmlOptions.Types.Validation.None                 // eine Vorlage hat keine Pflichtfelder
    });

    // 2. Erzeuger-Layer anhängen und die Angaben zur Übergabe eintragen
    await bmu.AddLayer(LayerType.Erzeuger);
    await bmu.UpdateDocument(new Document
    {
        Bgs = new BGSDokument
        {
            Menge = Amount(t.QuantityTons),
            Erzeuger = new Beteiligter { Datum = Date(t.HandoverDate) }
        }
    });

    // 3. Ein Mensch beim Erzeuger signiert
    await Sign(bmu, Rolle.Erz);

    // 4. Speichern und an Beförderer und Entsorger vermitteln
    return await bmu.SubmitTransportDocumentToZedal(distribute: true);
}

Was dabei wichtig ist:

  • Alle Beteiligten gehören in den Begleitschein, bevor er gespeichert wird. ZEDAL vermittelt an die Beteiligten, deren behördliche Nummer im Dokument steht. Fehlt die Nummer des Entsorgers, eines Zwischenlagers oder eines zweiten Beförderers, bekommt dieser nichts zugestellt.
  • Vorlage und Erzeuger-Layer sind zwei Schritte. Die Vorlage nimmt auf, was schon feststeht, und braucht die Rolle des Anlegenden. Im Erzeuger-Layer stehen die Angaben, für die der Erzeuger mit seiner Signatur einsteht, etwa die Menge. Statt aus Daten kann die Vorlage auch aus einem ZEDAL-Muster stammen.
  • Prüfung: Die Vorlage wird ohne Prüfung angelegt. AddLayer prüft nicht, UpdateDocument ohne Optionen dagegen vollständig. Ein Fehler kommt als CANCELLED (Fehlercodes → BMU).
  • Mengen in Tonnen, Volumen in Kubikmetern, jeweils höchstens drei Nachkommastellen (Feldregeln).

Beförderer

Der Beförderer holt den Begleitschein ab, hängt seinen Layer an, trägt Übernahme und Fahrzeug ein und signiert spätestens bei der Übernahme.

async Task ConfirmTakeover(BmuDocumentHandler bmu, Trip f)
{
    if (!IsSigned(bmu, "BGSERZLayer"))
        throw new InvalidOperationException("The waste producer has not signed yet.");

    // 1. Beförderer-Layer anhängen und die Übernahme eintragen
    await bmu.AddLayer(LayerType.Befoerderer);
    await bmu.UpdateDocument(new Document
    {
        Bgs = new BGSDokument
        {
            Befoerderer1 = new Befoerderer
            {
                Beteiligter = new Beteiligter { Datum = Date(f.TakeoverDate) },
                Kfz = Text(f.LicensePlate)
            }
        }
    });

    // 2. Der Fahrer signiert bei der Übernahme
    await Sign(bmu, Rolle.Bef);

    // 3. Speichern und vermitteln: Erzeuger und Entsorger sehen die Übernahme
    await bmu.SubmitTransportDocumentToZedal(distribute: true);
}

Aufgerufen wird die Methode mit dem Handler aus NextIncoming, sobald die Abholung ansteht.

  • Ein Auflieger kommt in KfzAuflieger; beide Kennzeichen haben höchstens zehn Zeichen.
  • Zweiter und dritter Beförderer schreiben in Befoerderer2 bzw. Befoerderer3. Wer übernimmt, prüft vorher, dass der Layer seines Vorgängers signiert ist: Beförderer oder Zwischenlager.
  • Signieren unterwegs: Der Signaturlink lässt sich auf jedem Gerät öffnen, auch auf dem Telefon des Fahrers. Wie das aussieht, steht unter WebSigner → In deine Anwendung einbinden.
  • Späteres Signieren nach der Übernahme ist nur mit schriftlicher Vereinbarung mit dem Vorgänger zulässig (§ 19 Abs. 2 NachwV).

Zwischenlager

Das Zwischenlager übernimmt vom ersten Beförderer und gibt an einen weiteren ab. Es bekommt den Begleitschein nur zugestellt, wenn es schon im Dokument steht, in der Regel seit dem Anlegen.

async Task ConfirmStorage(BmuDocumentHandler bmu, Storage l)
{
    if (!IsSigned(bmu, "BGSBEFLayer"))
        throw new InvalidOperationException("The carrier has not signed yet.");

    await bmu.AddLayer(LayerType.Zwischenlager);
    await bmu.UpdateDocument(new Document
    {
        Bgs = new BGSDokument
        {
            Zwischenlager = new Zwischenlager
            {
                Beteiligter = new Beteiligter { Datum = Date(l.TakeoverDate) },
                UebergabeDatum = Date(l.HandoverDate)
            }
        }
    });

    await Sign(bmu, Rolle.Zwl);
    await bmu.SubmitTransportDocumentToZedal(distribute: true);
}

Danach hängt der nächste Beförderer seinen Layer an und schreibt in Befoerderer2. Auf einen Zwischenlager-Layer folgt immer ein Beförderer, nie direkt der Entsorger.

Entsorger

Der Entsorger nimmt den Abfall an, trägt Annahmetag und gewogene Menge ein, signiert unverzüglich und schließt den Begleitschein ab. Mit dem Speichern geht er an die Behörden und an alle übrigen Beteiligten.

async Task ConfirmAcceptance(BmuDocumentHandler bmu, Acceptance a)
{
    if (!IsSigned(bmu, "BGSBEFLayer"))
        throw new InvalidOperationException("The carrier has not signed yet.");

    // 1. Entsorger-Layer anhängen und die Annahme eintragen
    await bmu.AddLayer(LayerType.Entsorger);
    await bmu.UpdateDocument(new Document
    {
        Bgs = new BGSDokument
        {
            Menge = Amount(a.WeighedQuantityTons),
            Entsorger = new Entsorger
            {
                Beteiligter = new Beteiligter { Datum = Date(a.AcceptanceDate) },
                AnnahmeVerweigert = Flag(false)
            }
        }
    });

    // 2. Unverzüglich nach der Annahme signieren
    await Sign(bmu, Rolle.Ent);

    // 3. Speichern, an alle Beteiligten vermitteln und an die Behörden melden
    await bmu.SubmitTransportDocumentToZedal(distribute: true, dekade: true);
}
  • dekade: true meldet den Begleitschein an die Behörden; der Name kommt von der Dekadenmeldung. ZEDAL schickt ihn über die ZKS-Abfall, aber nicht sofort: frühestens am Tag nach dem Speichern und in der Regel 10 Tage nach dem Annahmedatum. Gemeldet wird nur ein signierter Begleitschein, dessen oberster Layer der Entsorger- oder ein Ergänzungslayer ist und der ein Annahmedatum trägt. Stornierte Begleitscheine meldet ZEDAL nie.
  • Die Meldung muss für deinen Account eingerichtet sein. ZEDAL meldet nur für Entsorgernummern, die bei deinem Account für die Dekadenmeldung hinterlegt sind. Kläre das mit dem ZEDAL-Support, bevor du in Betrieb gehst; sonst bleibt die Meldung ohne Fehlermeldung aus.
  • distribute: true stellt Erzeuger und Beförderern den abgeschlossenen Begleitschein zu (§ 19 Abs. 4 NachwV).
  • Annahme verweigert: Setz AnnahmeVerweigert = Flag(true) und begründe die Verweigerung in Vermerke.

distribute und dekadenmeldung sind unabhängig voneinander

distribute: false unterdrückt nur die Zustellung an die Beteiligten, nicht die Meldung an die Behörden. Und über gRPC oder REST gilt ein nicht gesetztes Kennzeichen als true; das SDK setzt dagegen ohne Angabe beide auf false. Gib beide deshalb immer ausdrücklich an.

Einsammler

In der Sammelentsorgung holt der Einsammler bei vielen Erzeugern ab. Je Erzeuger entsteht ein Übernahmeschein, für die ganze Tour ein Begleitschein (Verfahren → Sammelentsorgung).

Übernahmeschein beim Erzeuger

async Task<string> CreateAcceptanceNote(Pickup pickup)
{
    var uns = client.DocumentHandler.CreateBmuHandler();
    var producer = Party(pickup.Producer);
    producer.Datum = Date(pickup.Date);
    var collector = Party(pickup.Collector);
    collector.Datum = Date(pickup.Date);
    string unsNr = (await client.CoreApi.Common.FetchNumbers(1,
        FetchDocumentNumbersRequest.Types.NumberType.BmuUns)).Single();

    await uns.Create(new Document
    {
        Uns = new UNSDokument
        {
            UnsNr = Number(unsNr),
            NachweisNr = Number(pickup.CollectiveCertificateNumber),        // SN...
            Abfallschluessel = Text(pickup.WasteCode),
            Abfallbezeichnung = Text(pickup.WasteDescription),
            Menge = Amount(pickup.QuantityTons),
            Erzeuger = producer,
            Befoerderer = new Befoerderer { Beteiligter = collector, Kfz = Text(pickup.LicensePlate) },
            Entsorger = new Entsorger { Beteiligter = Party(pickup.Disposer) }
        }
    }, vorlageLayer: false, new XmlOptions
    {
        AtbRolle = Rolle.Bef,                                         // Pflicht beim Übernahmeschein: ERZ, BEF oder ENT
        Pruefziffer = XmlOptions.Types.Pruefziffer.Calculate
    });

    await Sign(uns, Rolle.Bef);
    await uns.SubmitTransportDocumentToZedal(distribute: true);
    return unsNr;
}

Erzeuger und Einsammler füllen den Übernahmeschein gemeinsam aus (§ 12 Abs. 3 NachwV); elektronisch geführt signieren ihn beide qualifiziert. Abweichend davon darf der Übernahmeschein auch auf dem Formblatt geführt werden (§ 21 NachwV).

Der Übernahmeschein hat einen Beförderer und kein Zwischenlager. Weitere Layer lassen sich an einen Übernahmeschein über die API noch nicht anhängen (Übernahmeschein).

Begleitschein der Sammeltour

Der Einsammler trägt sich als Beförderer ein und setzt im Erzeugerfeld die fiktive Erzeugernummer aus Landeskenner, „S“ und Nullen (§ 13 Abs. 1 NachwV). Die Übernahmescheine der Tour stehen in UnsNrn:

var bgs = new BGSDokument
{
    BgsNr = Number(bgsNr),
    NachweisNr = Number(tour.CollectiveCertificateNumber),
    Abfallschluessel = Text(tour.WasteCode),
    Abfallbezeichnung = Text(tour.WasteDescription),
    Erzeuger = new Beteiligter { BehoerdlicheNr = Number(tour.StateCode + "S0000000") },
    Befoerderer1 = new Befoerderer { Beteiligter = Party(tour.Collector) },
    Entsorger = new Entsorger { Beteiligter = Party(tour.Disposer) }
};
bgs.UnsNrn.AddRange(tour.AcceptanceNoteNumbers.Select(Number));

Ab hier geht der Begleitschein den Weg aus den Abschnitten Erzeuger, Beförderer und Entsorger. Sammelt der Einsammler in mehreren Ländern, legt er je Land einen eigenen Begleitschein an (§ 13 Abs. 2 NachwV).

Übernahmescheinnummern nachträglich ändern

Eine mitgeschickte, nicht leere Liste legt fest, wie lang UnsNrn im aktuellen Layer ist. Eine Nummer, die in einem früheren Layer steht, annullierst du, indem du an ihrem Index den Löschwert 29999999999999 einträgst; so sieht es die BMU-Spezifikation vor.

Korrektur mit Ergänzungslayer

Nach der Signatur des Entsorgers ändert niemand mehr einen bestehenden Layer. Wer etwas korrigieren muss, hängt einen Ergänzungslayer mit seiner Rolle an, trägt die Korrektur ein, signiert und schickt den Begleitschein erneut an Behörden und Beteiligte (§ 11 Abs. 6 NachwV):

await bmu.AddLayer(LayerType.Ergaenzung, new XmlOptions { AtbRolle = Rolle.Ent });
await bmu.UpdateDocument(new Document
{
    Bgs = new BGSDokument
    {
        Menge = Amount(correctedQuantity),
        Vermerke = Text("Menge nach Nachwiegung berichtigt")
    }
});
await Sign(bmu, Rolle.Ent);
await bmu.SubmitTransportDocumentToZedal(distribute: true, dekade: true);

Ohne AtbRolle lehnt der Dienst den Ergänzungslayer mit CANCELLED ab.

Storno

Mit cancel: true speicherst du die aktuelle Version als Storno; im Dokument steht das danach in zusatz.storniert:

await bmu.SubmitTransportDocumentToZedal(cancel: true, dekade: false, distribute: true);

ZEDAL stellt den Storno den Beteiligten zu, an Beteiligte bei anderen Anbietern als Stornonachricht über die ZKS-Abfall. An die Behörden geht ein stornierter Begleitschein nie.

Die Nachweisverordnung selbst kennt keinen Storno. Ist der Abfall bereits übergeben, gilt der Begleitschein weiter, und eine Korrektur gehört in einen Ergänzungslayer. Ein Storno passt für einen Begleitschein, zu dem es keinen Transport gibt.

Was ZEDAL beim Speichern prüft

ImportTransportDocument sucht über die Begleitscheinnummer ein vorhandenes Dokument in deinem Bestand. Gibt es eins, entsteht eine neue Version, sonst ein neues Dokument. Abgelehnt wird eine Version,

  • deren Signaturen gebrochen sind, deren Inhalt sich also nach dem Signieren geändert hat,
  • die weniger Layer oder weniger Signaturen trägt als die gespeicherte Version.

Ein Layer ohne Signatur ist für diese Prüfungen kein Grund zur Ablehnung, und ob der richtige Beteiligte signiert hat, prüft ZEDAL nicht. Dass der richtige Mensch signiert hat, bevor du speicherst und vermittelst, liegt bei deinem System.

Signieren umsetzen

Signiert wird über den WebSigner: Dein System legt einen Signaturvorgang mit dem aktuellen XML an und zeigt dem Menschen einen Link. Er wählt dort sein Signaturmittel und gibt die Signatur bei seinem Vertrauensdiensteanbieter frei. Danach holt dein System das signierte XML ab und lädt es in den Handler:

async Task Sign(BmuDocumentHandler bmu, Rolle role)
{
    var result = await client.SignerApi.SigningProcesses.SignAsync(
        new[]
        {
            new DocumentInput
            {
                DisplayName = "Begleitschein",
                Kind = DocumentKind.BmuXml,
                Content = ByteString.CopyFrom(bmu.DocXml)
            }
        },
        presentSigningUrl: url => ShowSigningLink(url));

    var signed = result.Results.SingleOrDefault();
    if (!result.Completed || !DocumentResults.IsUsable(signed))
        throw new InvalidOperationException($"Not signed: {result.SigningProcess.State}");

    await bmu.Load(signed.Content.ToByteArray());
}

Dafür kommen Google.Protobuf (ByteString), Infotech.Signer.Sign.V1 (DocumentInput, DocumentKind) und Zedal.Sdk.Apis.Signer (DocumentResults) zu den Namespaces hinzu.

Layer-genaue Signaturen folgen

Derzeit signiert der WebSigner ein BMU-XML als Ganzes. Eine Signatur, die genau einen Layer für eine bestimmte Rolle abschließt, wie sie die Beispiele oben brauchen, ist noch nicht verfügbar; BmuSigningOptions wird derzeit nicht ausgewertet. Der Parameter role im Beispiel ist dafür vorgesehen. Bis dahin laufen die Beispiele bis zum Anlegen und Füllen der Layer; den Stand nennt die Seite WebSigner.

Über REST

Jeder Schritt dieser Seite hat eine REST-Entsprechung bis auf zwei: den Nummernabruf und den Import aus der Inbox. Für eine reine REST-Integration im eANV fehlen damit die Begleitscheinnummern.

Schritt REST
Nummer abrufen (FetchNumbers) – (nur gRPC und SDK)
Anlegen, Layer anhängen, Felder schreiben, lesen BmuDocumentApi, siehe Über REST
Speichern, vermitteln, melden, stornieren POST …/DocumentApi/ImportTransportDocument mit distribute, dekadenmeldung, cancel
Eingang abholen GET …/DocumentApi/PeekNextTransportDocument, dann GET …/DocumentApi/ExportCurrentDocument
Eingang quittieren PATCH …/DocumentApi/SetTransportDocumentReceived
Signieren SignerApi, siehe WebSigner

Weiter