OBS/Makros und Scripting/Anwendungsbereiche/ZUGFERD

Aus OBS Wiki
Zur Navigation springen Zur Suche springen


ZUGFERD

Es gibt die Möglichkeit den Import und Export von ZUGFERD Dateien per Scripting zu ändern, falls der OBS-Standard nicht ausreichen sollte.

Felder/Strukturen

    TZugPerson = class(TObject)
        cZUG_PsNr          : String;
        cZUG_Bemerkung     : String;
        cZUG_Name          : String;
        cZUG_Zeile1        : String;
        cZUG_Zeile2        : String;
        cZUG_Zeile3        : String;
        cZUG_Ort           : String;
        cZUG_PLZ           : String;
        cZUG_Land          : String;
        cZUG_AnspName      : String;
        cZUG_AnspTel       : String;
        cZUG_AnspEMail     : String;
        aFinanzIDs         : TxFinazID;

        Procedure AddFinanzID(cTmp1:String; cTmp2:String);

----------------------------------------

    TZugPosition = class(TObject)
        cZUP_UID          : String;
        cZUP_PosTyp       : String;
        cZUP_PosNr        : String;
        cZUP_ArtNr        : String;
        cZUP_ArtNrLief    : String;
        cZUP_ArtNrPsNr    : String;
        cZUP_ArtTyp       : String;
        cZUP_Bez1         : String;
        cZUP_Bez2         : String;
        cZUP_WGRName      : String;
        cZUP_LiefNr       : String;
        cZUP_Einheit      : String;
        cZUP_Text         : String;
        nZUP_Menge        : Double;
        nZUP_ENetto       : Double;
        nZUP_GNetto       : Double;
        nZUP_EBrutto      : Double;
        nZUP_MwstBetrag   : Double;
        nZUP_MwstSatz     : Double;
        cZUP_MWSTTyp      : String;
        cZUP_OBS_ERLKTO   : String;
        cZUP_OBS_Einheit  : String;
        cZUP_OBS_ArtNr    : String;
        cZUP_OBS_LiefNr   : String;
        cZUP_OBS_MwstSchl : String;
        cZUP_Fehler       : String;

----------------------------------------

    TZugHeaderFinanzen = class(TObject)
        oParent            : TZugFerd;
        cZUG_Waehrung      : String;
        nZUG_Netto         : Double;
        nZUG_Brutto        : Double;
        nZUG_MwstBetrag    : Double;
        nZUG_Netto1        : Double;
        nZUG_Netto2        : Double;
        nZUG_Netto3        : Double;
        nZUG_Brutto1       : Double;
        nZUG_Brutto2       : Double;
        nZUG_Brutto3       : Double;
        nZUG_MwstBetrag1   : Double;
        nZUG_MwstBetrag2   : Double;
        nZUG_MwstBetrag3   : Double;
        nZUG_MwstSatz1     : Double;
        nZUG_MwstSatz2     : Double;
        nZUG_MwstSatz3     : Double;
        cZUG_MwstSchl1     : String;
        cZUG_MwstSchl2     : String;
        cZUG_MwstSchl3     : String;
        cZUG_Zahlbed       : String;
        dZUG_FDatum        : TDateTime;

----------------------------------------

    TxZugPosition = Class(TObjectList<TZugPosition>);

    TZugFerd = class(TObject)
        oMyDB            : TxDB;
        oPersonen        : TZugHeaderPersonen;
        oFinanzen        : TZugHeaderFinanzen;
        oLieferung       : TZugHeaderLieferung;
        oPositionen      : TxZugPosition;
        cError           : String;
        lError           : Boolean;

        cZUG_UID         : String;
        oZUG_XML         : TStringList;
        cZUG_PDFName     : String;
        cZUG_DMSTyp      : String;
        cZUG_MD5         : String;
        cZUG_Nr          : String;
        cZUG_PDFData     : String;
        cZUG_Schema      : String;
        cZUG_TypCode     : String;

        cZUG_Fehler      : String;
        cZUG_Bestaetigt  : String;
        cZUG_SB          : String;
        cZUG_SysUID      : String;

        dZUG_RDatum      : TDateTime;
        dZUG_SDatum      : TDateTime;
        dZUG_ADatum      : TDateTime;

        function  InsertToPositionen     (nPos:Integer; oPos:TZugPosition):integer;
        function  AddToPositionen        (oPos:TZugPosition):integer;
        function  nPosCnt                ():Integer;
        function  GetPosition            (nPos:Integer):TZugPosition;
        function  AddProto               (const cMessage:String):Boolean;
        function  AddFile                (const cFileName:String; const cDMSType:String):Boolean;

Import

Wenn es einen aktiven Eintrag in der Script Libary mit dem Namen IMPORT_ZUGFERD gibt wird der Import über die Pax Funktion ausgeführt.

Beispiel Script Import

//------------------------------------------------------------------------------
// Unit Name: IMPORT_ZUGFERD
// Author:    JB
// Date:      09-12-2021
//------------------------------------------------------------------------------

var oZugFerd : TZugFerd;
    oCW      : TfrmCW;

//------------------------------------------------------------------------------
//  Procedure: PersonHeader_Import_XML
//  Author:    JB
//  Date:      09-12-2021
//  Comment:
//------------------------------------------------------------------------------

function PersonHeader_Import_XML(oZugPersHeader:TZugPerson; cTradePartyXML:String):Boolean;
var nPos   : Integer;
    nStart : integer;
    cTemp1 : String;
    cTemp2 : String;
    cTemp3 : String;
begin
    CWPrintn(oCW, 'Importiere Header');
    GetXMLToken(cTradePartyXML,'ram:Name', oZugPersHeader.cZUG_Name);

    if (GetXMLToken(cTradePartyXML,'ram:PostalTradeAddress',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:LineOne'     , oZugPersHeader.cZUG_Zeile1);
        GetXMLToken(cTemp1,'ram:CityName'    , oZugPersHeader.cZUG_Ort);
        GetXMLToken(cTemp1,'ram:PostcodeCode', oZugPersHeader.cZUG_PLZ);
        GetXMLToken(cTemp1,'ram:CountryID'   , oZugPersHeader.cZUG_Land);
    end;
    
    CWPrintn(oCW, 'Importiere Tax');

    nPos := 1;
    while (GetXMLToken(cTradePartyXML,'ram:SpecifiedTaxRegistration', nPos, cTemp1, False)) do begin
        nStart := 1;
        GetXMLTokenValue(cTemp1,'ram:ID', nStart, cTemp2, cTemp3); // value, key
        oZugPersHeader.AddFinanzID(cTemp3, cTemp2);
    end;
end;

//------------------------------------------------------------------------------
//  Procedure: Personen_Import_XML
//  Author:    JB
//  Date:      09-12-2021
//  Comment:
//------------------------------------------------------------------------------

function Personen_Import_XML(cXMLTradeAgreement:String):Boolean;
var cTemp1 : String;
begin
    CWPrintn(oCW, 'Importiere Auftragsnummer Kunde');

    GetXMLToken(cXMLTradeAgreement,'ram:BuyerReference',oZugFerd.oPersonen.cAufNrKu);

    if (GetXMLToken(cXMLTradeAgreement,'ram:BuyerTradeParty',cTemp1)) then begin
        PersonHeader_Import_XML(oZugFerd.oPersonen.oZUG_Buyer, cTemp1);
    end;

    if (GetXMLToken(cXMLTradeAgreement,'ram:SellerTradeParty',cTemp1)) then begin
        PersonHeader_Import_XML(oZugFerd.oPersonen.oZUG_Seller,cTemp1);
    end;
end;

//------------------------------------------------------------------------------
//  Procedure: Lieferung_Import_XML
//  Author:    JB
//  Date:      09-12-2021
//  Comment:
//------------------------------------------------------------------------------

function Lieferung_Import_XML(cXML:String):Boolean;
begin
    //Wenn Benötigt
    CWPrintn(oCW, 'Importiere Lieferung');
end;

//------------------------------------------------------------------------------
//  Procedure: Positionen_Import_XML
//  Author:    JB
//  Date:      09-12-2021
//  Comment:
//------------------------------------------------------------------------------

function Positionen_Import_XML(oPos:TZugPosition; cXMLTradeLineItem:String):Boolean;
var cTemp1    : String;
    cTemp2    : String;
    cValue    : String;
    nPos      : integer;
begin

    nPos := 1;
    
    CWPrintn(oCW, 'Importiere Positionen');

    if (GetXMLToken(cXMLTradeLineItem,'ram:AssociatedDocumentLineDocument',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:LineID',oPos.cZUP_PosNr);
    end;

    if (GetXMLToken(cXMLTradeLineItem,'ram:SpecifiedTradeProduct',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:Name'             , oPos.cZUP_Bez1 );
        GetXMLToken(cTemp1,'ram:SellerAssignedID' , oPos.cZUP_ArtNrLief);
        GetXMLTokenValue(cTemp1, 'ram:GlobalID'   , nPos, oPos.cZUP_ArtNr, oPos.cZUP_ArtTyp);
        GetXMLToken(cTemp1,'ram:Description'      , oPos.cZUP_Text);

        //Warengruppe

        if GetXMLToken(cTemp1,'ram:DesignatedProductClassification', cTemp2) then begin
            //nPos := 1;
            GetXMLToken(cTemp2,'ram:ClassName', oPos.cZUP_WGRName);
        end;
    end;

    if (GetXMLToken(cXMLTradeLineItem,'ram:SpecifiedLineTradeAgreement',cTemp1)) then begin
        if (GetXMLToken(cTemp1,'ram:GrossPriceProductTradePrice',cTemp2)) then begin
            GetXMLToken(cTemp2,'ram:ChargeAmount', cValue);
            oPos.nZUP_EBrutto := fVAl(cValue);
        end;
        if (GetXMLToken(cTemp1,'ram:NetPriceProductTradePrice',cTemp2)) then begin
            GetXMLToken(cTemp2,'ram:ChargeAmount', cValue);
            oPos.nZUP_ENetto := fVAl(cValue);
        end;
    end;

    if (GetXMLToken(cXMLTradeLineItem,'ram:SpecifiedLineTradeDelivery', cTemp1)) then begin
        GetXMLTokenValue(cTemp1,'ram:BilledQuantity', nPos, cValue, oPos.cZUP_Einheit);
        oPos.nZUP_Menge := fVAl(cValue);
    end;

    if (GetXMLToken(cXMLTradeLineItem,'ram:SpecifiedLineTradeSettlement',cTemp1)) then begin
        if (GetXMLToken(cTemp1,'ram:ApplicableTradeTax',cTemp2)) then begin
            GetXMLToken(cTemp2,'ram:RateApplicablePercent', cValue);
            oPos.nZUP_MwstSatz := fVAl(cValue);
            GetXMLToken(cTemp2,'ram:TypeCode', oPos.cZUP_MWSTTyp );
        end;

        GetXMLToken(cTemp1,'ram:LineTotalAmount', cValue);
        oPos.nZUP_GNetto := fVAl(cValue);
    end;

    if (oPos.nZUP_ENetto > 0) and (oPos.nZUP_MwstSatz > 0) then begin
        oPos.nZUP_MwstBetrag := oPos.nZUP_ENetto * (oPos.nZUP_MwstSatz / 100);
    end;

    oPos.cZUP_Fehler      := '';
end;

//------------------------------------------------------------------------------
//  Procedure: Finanzen_Import_XML
//  Author:    JB
//  Date:      09-12-2021
//  Comment:
//------------------------------------------------------------------------------

function Finanzen_Import_XML(oFinanzen:TZugHeaderFinanzen; cXMLTradeSettlement:String):Boolean;
var cTemp1     : String;
    cTemp2     : String;
    cTemp3     : String;
    cTemp4     : String;
    nPos       : Integer;
    oPos       : TZugPosition;
    oPosTmp    : TZugPosition;
    cIndikator : String;
    lIndikator : Boolean;
    cText      : String;
    nPosNr     : Integer;
    cPosNr     : String;
    i          : integer;
begin

    CWPrintn(oCW, 'Importiere Finanzen');

    lIndikator := false;
    
    if (GetXMLToken(cXMLTradeSettlement,'ram:InvoiceCurrencyCode',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:InvoiceCurrencyCode', oFinanzen.cZUG_Waehrung);
    end;

    if (GetXMLToken(cXMLTradeSettlement,'ram:SpecifiedTradeSettlementPaymentMeans',cTemp1)) then begin
    
        oPos             := TZugPosition.Create();
        oPos.cZUP_UID    :=  TZugFerd(oFinanzen.oParent).cZUG_UID;
        oPos.cZUP_PosTyp := ZUG_POSTYP_MEMO;
        
        if (GetXMLToken(cTemp1,'ram:PayeePartyCreditorFinancialAccount',cTemp2)) then begin
            if (GetXMLToken(cTemp2,'ram:IBANID',cTemp3)) then begin
                cText := 'Betrag zu überweisen an:' + CRLF + 'IBAN ' + cTemp3;
            end;
        end;

        oPos.cZUP_Text := cText;
        //TObjectList(TZugFerd(oFinanzen.oParent).oPositionen).Insert(0, oPos);
        TZugFerd(oFinanzen.oParent).InsertToPositionen(0, oPos);
    end;

    if (GetXMLToken(cXMLTradeSettlement,'ram:SpecifiedTradeSettlementHeaderMonetarySummation',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:TaxBasisTotalAmount', cTemp2);
        oZugFerd.oFinanzen.nZUG_Netto := fVal(cTemp2);
        GetXMLToken(cTemp1,'ram:TaxTotalAmount', cTemp2);
        oFinanzen.nZUG_MwstBetrag := fVal(cTemp2);
        GetXMLToken(cTemp1,'ram:GrandTotalAmount', cTemp2);
        oFinanzen.nZUG_Brutto := fVal(cTemp2);
    end;

    if (GetXMLToken(cXMLTradeSettlement,'ram:SpecifiedTradePaymentTerms',cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:Description', oFinanzen.cZUG_Zahlbed);
        if (GetXMLToken(cTemp1,'ram:DueDateDateTime',cTemp2, False)) then begin
            GetXMLTokenValue(cTemp2,'udt:DateTimeString', nPos,cTemp4, cTemp3);
            oFinanzen.dZUG_FDatum := SToD(cTemp4);
        end;
    end;

    nPos := 1;
    if (GetXMLToken(cXMLTradeSettlement,'ram:ApplicableTradeTax', nPos, cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:CalculatedAmount', cTemp2);
        oFinanzen.nZUG_MwstBetrag1 := fVal(cTemp2);

        GetXMLToken(cTemp1,'ram:BasisAmount', cTemp2);
        oFinanzen.nZUG_Netto1  := fVal(cTemp2);
        oFinanzen.nZUG_Brutto1 := oFinanzen.nZUG_Netto1 + oFinanzen.nZUG_MwstBetrag1;

        GetXMLToken(cTemp1,'ram:RateApplicablePercent', cTemp2);
        oFinanzen.nZUG_MwstSatz1 := fVal(cTemp2);
    end;

    if (GetXMLToken(cXMLTradeSettlement,'ram:ApplicableTradeTax', nPos, cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:CalculatedAmount', cTemp2);
        oFinanzen.nZUG_MwstBetrag2 := fVal(cTemp2);

        GetXMLToken(cTemp1,'ram:BasisAmount', cTemp2);
        oFinanzen.nZUG_Netto2  := fVal(cTemp2);
        oFinanzen.nZUG_Brutto2 := oFinanzen.nZUG_Netto2 + oFinanzen.nZUG_MwstBetrag2;

        GetXMLToken(cTemp1,'ram:RateApplicablePercent', cTemp2);
        oFinanzen.nZUG_MwstSatz2 := fVal(cTemp2);
    end;

    if (GetXMLToken(cXMLTradeSettlement,'ram:ApplicableTradeTax', nPos, cTemp1)) then begin
        GetXMLToken(cTemp1,'ram:CalculatedAmount', cTemp2);
        oFinanzen.nZUG_MwstBetrag3 := fVal(cTemp2);

        GetXMLToken(cTemp1,'ram:BasisAmount', cTemp2);
        oFinanzen.nZUG_Netto3  := fVal(cTemp2);
        oFinanzen.nZUG_Brutto3 := oFinanzen.nZUG_Netto3 + oFinanzen.nZUG_MwstBetrag3;

        GetXMLToken(cTemp1,'ram:RateApplicablePercent', cTemp2);
        oFinanzen.nZUG_MwstSatz3 := fVal(cTemp2);
    end;

    if (oFinanzen.nZUG_MWSTBetrag = 0) then begin
        oFinanzen.nZUG_MWSTBetrag := oFinanzen.nZUG_MwstBetrag1 + oFinanzen.nZUG_MwstBetrag2 + oFinanzen.nZUG_MwstBetrag3;
    end;

    CWPrintn(oCW, 'Importiere Rabatte und Zusatzkosten');

    // Rabatte und Zusatzkosten als Positionen erfassen
    nPos := 1;
    while (GetXMLToken(cXMLTradeSettlement, 'ram:SpecifiedTradeAllowanceCharge', nPos, cTemp1)) do begin

        // Höchste Positionsnummer ermitteln, damit Zusatzkosten mit
        // aufsteigender Pos-Nr erfasst werden können
        nPosNr := 0;
        
        //for i:=0 to TObjectList(TZugFerd(oFinanzen.oParent).oPositionen).Count do begin
        for i:=0 to TZugFerd(oFinanzen.oParent).nPosCnt()-1 do begin
           oPosTmp := TZugFerd(oFinanzen.oParent).GetPosition(i);
           cPosNr  := oPosTmp.cZUP_PosNr;      
           nPosNr  := Max(nPosNr, iVal(cPosNr));
           CWPrintn(oCW, 'MaxNr ' + cPosNr);
        end;
        
        oPos := TZugPosition.Create( TZugFerd(oFinanzen.oParent).cZUG_UID);
        oPos             := TZugPosition.Create();
        oPos.cZUP_UID    := TZugFerd(oFinanzen.oParent).cZUG_UID;        
        oPos.cZUP_PosNr  := TStr(nPosNr + 1, 0, 0);
        cText := '';

        if (GetXMLToken(cTemp1,'ram:ChargeIndicator',cTemp2)) then begin
            GetXMLToken(cTemp1,'udt:Indicator', cIndikator);
            lIndikator := lower(cIndikator) = 'true';
            if (lIndikator) then begin
                cText := cText + 'Zusatzkosten' + CRLF;
            end else begin
                cText := cText + 'Rabatt' + CRLF;
            end;
        end;

        GetXMLToken(cTemp1,'ram:Reason',oPos.cZUP_Bez1);

        if (GetXMLToken(cTemp1,'ram:BasisAmount',cTemp2)) then begin
            cText := cText + 'Grundlage: ' + cTemp2;
        end;

        if (GetXMLToken(cTemp1,'ram:ActualAmount',cTemp2)) then begin
            oPos.nZUP_ENetto := fVal(cTemp2);
            if (lIndikator) then begin // Rabatt
                oPos.nZUP_Menge := 1;
            end else begin
                oPos.nZUP_Menge := -1;
            end;
        end;

        if (GetXMLToken(cTemp1,'ram:CategoryTradeTax',cTemp2)) then begin
            if (GetXMLToken(cTemp2,'ram:RateApplicablePercent',cTemp3)) then begin
                opos.nZUP_MwstSatz := fVal(cTemp3);
            end;
        end;

        oPos.cZUP_Text := cText;
        oPos.nZUP_GNetto := oPos.nZUP_Menge * oPos.nZUP_ENetto;

         TZugFerd(oFinanzen.oParent).AddToPositionen(oPos);
         //TObjectList(TZugFerd(oFinanzen.oParent).oPositionen).Add(oPos);
    end;
end;

//------------------------------------------------------------------------------
//  Procedure: Startproc
//  Author:    JB
//  Date:      09-12-2021
//  Comment:   Startfunction 
//------------------------------------------------------------------------------

function  Startproc(oOBJ:TZugFerd; cXMLData:String):Boolean;
var cTemp1   : String;
    cTemp2   : String;
    cTemp3   : String;
    nPos     : Integer;
    oPos     : TZugPosition;
    lImport  : Boolean;
begin
   Result   := False;
   oZugFerd := oOBJ;

    oCW := CWOpen(20, 80, 'Importiere XML Zugferddaten');
    
    try
        if (GetXMLToken(cXMLData, 'ram:GuidelineSpecifiedDocumentContextParameter', cTemp1)) then begin
            GetXMLToken(cTemp1,'ram:ID', oZugFerd.cZUG_Schema);
        end;

        if (GetXMLToken(cXMLData, 'rsm:ExchangedDocument', cTemp1)) then begin
    
            nPos := 1;
          
            GetXMLTokenValue(cTemp1,'udt:DateTimeString', nPos, cTemp2, cTemp3);
            oZugFerd.dZUG_RDatum := SToD(cTemp2);

            nPos := 1;

            GetXMLToken(cTemp1,'ram:ID', oZugFerd.cZUG_Nr);
            GetXMLToken(cTemp1,'ram:TypeCode', oZugFerd.cZUG_TypCode);

            CWPrintn(oCW, 'Importiere Positionen Freitext');

            while (GetXMLToken(cTemp1, 'ram:IncludedNote', nPos, cTemp2)) do begin
                oPos := TZugPosition.Create(oZugFerd.cZUG_UID);

                oPos.cZUP_PosTyp := ZUG_POSTYP_MEMO;
                oPos.cZUP_Bez1   := 'Freitext';
                GetXMLToken(cTemp2, 'ram:Content', oPos.cZUP_Text);
                GetXMLToken(cTemp2, 'ram:SubjectCode', oPos.cZUP_Bez2);

                oZugFerd.AddToPositionen(oPos);
            end;
        end;

        if (GetXMLToken(cXMLData, 'rsm:SupplyChainTradeTransaction', cTemp1)) then begin

            lImport := False;

            if (GetXMLToken(cTemp1, 'ram:ApplicableHeaderTradeDelivery', cTemp2)) then begin
                CWPrintn(oCW, 'Importiere Lieferung');
                Lieferung_Import_XML(cTemp2);       
                lImport := True;
            end;

            if (GetXMLToken(cTemp1, 'ram:ApplicableHeaderTradeAgreement', cTemp2)) then begin
                CWPrintn(oCW, 'Importiere Personen');
                Personen_Import_XML(cTemp2);
                lImport := True;
            end;

            if (not lImport) then begin
                oZugFerd.AddProto('Kein Token ApplicableHeaderTradeDelivery vorhanden!');
                oZugFerd.AddProto('Kein Token ApplicableHeaderTradeAgreement vorhanden!');
                Result := False;
            end;

            nPos := 1;

            while (GetXMLToken(cTemp1, 'ram:IncludedSupplyChainTradeLineItem', nPos, cTemp2)) do begin
                oPos := TZugPosition.Create(oZugFerd.cZUG_UID);
                Positionen_Import_XML(oPos, cTemp2);
            
                oZugFerd.AddToPositionen(oPos);
            end;

            // Beträge nach den Positionen, damit Rabatte und Zusatzkosten als Positionen am Ende erfasst werden
            if (GetXMLToken(cTemp1, 'ram:ApplicableHeaderTradeSettlement', cTemp2)) then begin
                Finanzen_Import_XML(oZugFerd.oFinanzen, cTemp2);
            end;

            Result := True;
        end;
    finally
        CWClose(oCW);
    end;
end;

//------------------------------------------------------------------------------

Export

Wenn es einen aktiven Eintrag in der Script Libary mit dem Namen EXPORT_ZUGFERD gibt wird die Erstellung der XML-Datei über die Pax Funktion ausgeführt.

Beispiel Script Export

//------------------------------------------------------------------------------
// Unit Name: EXPORT_ZUGFERD
// Author:    JB
// Date:      09-12-2021
//------------------------------------------------------------------------------

var oXML         : TXMLWriter;
    cRechnungsNr : String;
    qRech        : TxFQuery;
    qKonst       : TxFQuery;
    qStamm       : TxFQuery;
    qPerson      : TxFQuery;
    dLiefDat     : TDateTime;
    nMWST        : Double;
    nMwstTotal   : Double;
    oFormat      : TFormatSettings;
        
//------------------------------------------------------------------------------
//  Procedure: _Format
//  Author:    JB
//  Date:      09-12-2021
//  Comment:   Zahlenausgabe für XML 
//------------------------------------------------------------------------------    
    
function _Format(nVal : Double): String;
 begin
     Result := ZugFerd_FormatDouble(nVal);
 end;

//------------------------------------------------------------------------------
//  Procedure: OBSRechnungsart_To_ZUGInvoiceTypeCode(
//  Author:    JB
//  Date:      09-12-2021
//  Comment:    
//------------------------------------------------------------------------------ 

function OBSRechnungsart_To_ZUGInvoiceTypeCode(const cOBSRechArt : String): String;
begin
    Result := '';
    if          (cOBSRechArt = '1') then begin // Standardrechnung
        Result := ZUG_RECHTYP_COMMERCIAL;
    end else if (cOBSRechArt = '2') then begin // Abschlagsrechnung
        Result := ZUG_RECHTYP_ABSCHLAG;
    end else if (cOBSRechArt = '3') then begin // Schlussrechnung
        Result := ZUG_RECHTYP_COMMERCIAL;
    end else if (cOBSRechArt = '4') then begin // Proformarechnung
        Result := ZUG_RECHTYP_PROFORMA;
    end else if (cOBSRechArt = '5') then begin // Gutschrift
        Result := ZUG_RECHTYP_GUTSCHRIFT;
    end else if (cOBSRechArt = '6') then begin // Stornorechnung
        Result := ZUG_RECHTYP_GUTSCHRIFT;
    end else if (cOBSRechArt = '7') then begin // Anzahlungsrechnung
        Result := ZUG_RECHTYP_PARTIAL;
    end;
end;

//------------------------------------------------------------------------------
//  Procedure: OBSMwstSchl_To_ZUGCategoryCode((
//  Author:    JB
//  Date:      09-12-2021
//  Comment:    
//------------------------------------------------------------------------------ 

function  OBSMwstSchl_To_ZUGCategoryCode(const cOBSMWST : String): String;
var nMwst : Integer;
begin
    nMWST := iVal(cOBSMWST);

    if (nMWST < 10) then begin
        Result := ZUG_MWSTTYP_STANDARD;
    end;
end;

//------------------------------------------------------------------------------
//  Procedure: GetVersandTyp(
//  Author:    JB
//  Date:      09-12-2021
//  Comment:    
//------------------------------------------------------------------------------ 

function GetVersandTyp(oMyDB: TxDB; cPerson: String): Integer;
begin
    Result := iVal(DB_ReadSQLValue(oMyDB,'ZUGFERD_PERSONEN','zp_versand','zp_psnr = '+DB_SQLVal(cPerson)));
end;

//------------------------------------------------------------------------------
//  Procedure: ZugFerd_Positionen
//  Author:    JB
//  Date:      09-12-2021
//  Comment:    
//------------------------------------------------------------------------------ 

procedure ZugFerd_Positionen(var oXML : TXMLWriter);
 var cSQL : String;
     qPos : TxFQuery;
 begin

     cSQL := 'SELECT * FROM RECHPOS'+
             ' LEFT JOIN s_mwst ON az_mwstsch = mw_nr' +
             ' WHERE az_nr = ' + DB_SQLVal(cRechnungsNr) +
             ' AND az_typ = 0';

     if (DB_SOpen(oDB, cSQL, qPos)) then begin

         dLiefDat := CToD(DB_ReadSQLValue(oDB,'LIEFSTA LEFT JOIN LIEFPOS ON a_nr = az_nr','a_datum','az_aufnr = '+DB_SQLVal(qPos.A2C('az_aufnr'))));
         if (empty(dLiefDat)) then begin
             dLiefDat := qRech.A2D('a_facdatum');
             if (empty(dLiefdat)) then begin
                 dLiefDat := qRech.A2D('a_datum');
             end;
         end;

         while not qPos.eof do begin
             with oXML do begin
                 OpenElement('ram:IncludedSupplyChainTradeLineItem');
                     OpenElement('ram:AssociatedDocumentLineDocument');
                         AddElement('ram:LineID', qPos.A2C('az_posnr')); // Positionsnummer // BR-21
                     CloseElement; // ram:AssociatedDocumentLineDocument

                     OpenElement('ram:SpecifiedTradeProduct');
                         AddElement('ram:SellerAssignedID', qPos.A2C('az_artnr')); //BT-155 Artikelnummer des Verkäufers
                         AddElement('ram:Name', qPos.A2C('az_bez1')); // Positionsname // BR-25
                     CloseElement; // ram:SpecifiedTradeProduct

                     OpenElement('ram:SpecifiedLineTradeAgreement');
                         OpenElement('ram:NetPriceProductTradePrice');
                             AddElement('ram:ChargeAmount', _Format(qPos.A2F('az_epreis'))); // Netto Einzelpreis // BR-26, BR-27
                         CloseElement; // ram:NetPriceProductTradePrice
                     CloseElement; // ram:SpecifiedLineTradeAgreement

                     OpenElement('ram:SpecifiedLineTradeDelivery');
                         AddAttrElement('ram:BilledQuantity', 'unitCode', 'H87', _Format(qPos.A2F('az_menge'))); // Einheit und Menge // BR-22, BR-23
                     CloseElement; // ram:SpecifiedLineTradeDelivery

                     OpenElement('ram:SpecifiedLineTradeSettlement');
                         OpenElement('ram:ApplicableTradeTax');
                             OpenElement('ram:TypeCode');
                                 SetContent('VAT'); // Steuerart, im Standard immer VAT
                             CloseElement; // ram:TypeCode
                             OpenElement('ram:CategoryCode');
                                 // Andere: (100 - 199)
                                 // AE:     $13b Reverse Charge (200 - 299)
                                 // K:      Innereuropäisch (400 - 499)
                                 SetContent('S');
                             CloseElement; // ram:CategoryCode
                             OpenElement('ram:RateApplicablePercent');
                                 SetContent(_Format(qPos.A2F('mw_satz')));
                             CloseElement; // ram:RateApplicablePercent
                         CloseElement; // ram:ApplicableTradeTax

                         if (qPos.A2F('az_rabatt') > 0) or (qPos.A2F('az_grabatt') > 0) then begin
                         OpenElement('ram:SpecifiedTradeAllowanceCharge');
                             OpenElement('ram:ChargeIndicator');
                                 AddElement('udt:Indicator','false'); // false = Abschlag, true = Zuschlag
                             CloseElement; // ram:ChargeIndicator

                             if (qPos.A2F('az_rabatt') > 0) then begin
                                 AddElement('ram:CalculationPercent', qPos.A2C('az_rabatt'));
                             end;

                             AddElement('ram:BasisAmount', _Format(qPos.A2F('az_menge')*qPos.A2F('az_epreis')));

                             if (qPos.A2F('az_rabatt') > 0) then begin // BR-41
                                 AddElement('ram:ActualAmount', _Format(qPos.A2F('az_menge')*qPos.A2F('az_epreis')*(qPos.A2F('az_rabatt')/100)));
                             end else if (qPos.A2F('az_grabatt') > 0) then begin
                                 AddElement('ram:ActualAmount', _Format(qPos.A2F('az_grabatt')));
                             end;
                             AddElement('ram:ReasonCode', '95');
                             AddElement('ram:Reason', 'Discount'); // BR-42
                         CloseElement; // ram:SpecifiedTradeAllowanceCharge
                         end;

                         OpenElement('ram:SpecifiedTradeSettlementLineMonetarySummation');
                             AddElement('ram:LineTotalAmount', _Format(qPos.A2F('az_gpreis'))); // Netto Gesampreis // BR-24
                         CloseElement; // ram:SpecifiedTradeSettlementLineMonetarySummation
                     CloseElement; // ram:SpecifiedLineTradeSettlement
                 CloseElement; // ram:IncludedSupplyChainTradeLineItem
             end;
             qPos.next();
         end;
     end;
     DB_Close(qPos);
 end;

//------------------------------------------------------------------------------
//  Procedure: StartProc
//  Author:    JB
//  Date:      09-12-2021
//  Comment:   Start Procwedure 
//------------------------------------------------------------------------------ 

function StartProc(xoXML         : TXMLWriter;
                   xcRechnungsNr : String;
                   xqRech        : TxFQuery;
                   xqKonst       : TxFQuery;
                   xqStamm       : TxFQuery;
                   xqPerson     : TxFQuery):Boolean;
begin
    Result        := True;
    oXML          := xoXML;                
    cRechnungsNr  := xcRechnungsNr;
    qRech         := xqRech;       
    qKonst        := xqKonst;      
    qStamm        := xqStamm;      
    qPerson       := xqPerson;     

    oFormat.DecimalSeparator := '.';
    oFormat.CurrencyDecimals := 2;
    
    oXML.OpenElement('rsm:CrossIndustryInvoice');
        oXML.SetAttribute('xmlns:a',   'urn:un:unece:uncefact:data:standard:QualifiedDataType:100');
        oXML.SetAttribute('xmlns:rsm', 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100');
        oXML.SetAttribute('xmlns:qdt', 'urn:un:unece:uncefact:data:standard:QualifiedDataType:10');
        oXML.SetAttribute('xmlns:ram', 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100');
        oXML.SetAttribute('xmlns:xs',  'http://www.w3.org/2001/XMLSchema');
        oXML.SetAttribute('xmlns:udt', 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100');
        oXML.OpenElement('rsm:ExchangedDocumentContext');
            oXML.OpenElement('ram:GuidelineSpecifiedDocumentContextParameter');
                oXML.AddElement('ram:ID', ZUG_COMPLIANCE_XRECHNUNG_2_0); // BR-1
            oXML.CloseElement; // ram:GuidelineSpecifiedDocumentContextParameter
        oXML.CloseElement; // rsm:ExchangedDocumentContext

        oXML.OpenElement('rsm:ExchangedDocument');
            oXML.AddElement('ram:ID', qRech.A2C('a_nr')); // Rechnungsnummer  // BR-2
            oXML.AddElement('ram:TypeCode', OBSRechnungsart_To_ZUGInvoiceTypeCode(qRech.A2C('a_art'))); // Rechnungstyp // BR-4
            oXML.OpenElement('ram:IssueDateTime');
                oXML.AddAttrElement('udt:DateTimeString','format', '102', DToS(qRech.A2D('a_datum'))); // Datum // BR-3
            oXML.CloseElement; // ram:IssueDateTime
            
            //Wenn Postgut
            //oXML.OpenElement('ram:IncludedNote');
            //    oXML.AddElement('ram:Content',qRech.A2C('a_aufnrku'));
            //    oXML.AddElement('ram:SubjectCode','INV');
            //oXML.CloseElement; //ram:IncludedNote
            
        oXML.CloseElement; // rsm:ExchangedDocument

        oXML.OpenElement('rsm:SupplyChainTradeTransaction');
        
            ZugFerd_positionen(oXML); // ram:IncludedSupplyChainTradeLineItem

            oXML.OpenElement('ram:ApplicableHeaderTradeAgreement');
                oXML.AddElement('ram:BuyerReference',qPerson.A2C('zp_leitwegid')); // Leitweg-ID für XRechnung

                oXML.OpenElement('ram:SellerTradeParty');
                    oXML.AddElement('ram:ID', qPerson.A2C('zp_kreditorennr')); // BT-29: Kreditorennummer beim Kunden
                    oXML.AddElement('ram:Name', qKonst.A2C('k_name')); // BR-6

                    oXML.OpenElement('ram:DefinedTradeContact'); // BG-6, BR-DE-5, BR-DE-6, BR-DE-7
                        oXML.AddElement('ram:PersonName', qPerson.A2C('zp_apname')); // BT-41 Name

                        oXML.OpenElement('ram:TelephoneUniversalCommunication'); // BT-42 Telefon
                            oXML.AddElement('ram:CompleteNumber', qPerson.A2C('zp_aptel'));
                        oXML.CloseElement();  // ram:TelephoneUniversalCommunication

                        oXML.OpenElement('ram:EmailURIUniversalCommunication'); // BT-43 E-Mail
                            oXML.AddElement('ram:URIID', qPerson.A2C('zp_apmail'));
                        oXML.CloseElement();  // ram:EmailURIUniversalCommunication
                    oXML.CloseElement();  // ram:DefinedTradeContact

                    oXML.OpenElement('ram:PostalTradeAddress'); // BR-8
                        oXML.AddElement('ram:PostcodeCode', qKonst.A2C('k_plz'));
                        oXML.AddElement('ram:LineOne', qKonst.A2C('k_strasse'));
                        oXML.AddElement('ram:CityName', qKonst.A2C('k_ort'));
                        oXML.AddElement('ram:CountryID', 'DE'); // BR-9
                    oXML.CloseElement; // ram:PostalTradeAddress

                    oXML.OpenElement('ram:SpecifiedTaxRegistration');
                        oXML.AddAttrElement('ram:ID', 'schemeID', 'VA', qKonst.A2C('k_ustid')); //
                    oXML.CloseElement; // ram:SpecifiedTaxRegistration
                oXML.CloseElement; // ram:SellerTradeParty

                oXML.OpenElement('ram:BuyerTradeParty');
                    oXML.AddElement('ram:Name',qRech.A2C('a_name')); // BR-7
                    oXML.OpenElement('ram:PostalTradeAddress'); // BR-10
                        oXML.AddElement('ram:PostcodeCode', qRech.A2C('a_plz'));
                        oXML.AddElement('ram:LineOne', qRech.A2C('a_strasse'));
                        oXML.AddElement('ram:CityName', qRech.A2C('a_ort'));
                        oXML.AddElement('ram:CountryID', 'DE'); // BR-11
                    oXML.CloseElement; // ram:PostalTradeAddress
                oXML.CloseElement; // ram:BuyerTradeParty

                oXML.OpenElement('ram:BuyerOrderReferencedDocument');
                    oXML.AddElement('ram:IssuerAssignedID',qRech.A2C('a_aufnrku'));
                oXML.CloseElement(); //ram:BuyerOrderReferencedDocument

            oXML.CloseElement; // ram:ApplicableHeaderTradeAgreement

            oXML.OpenElement('ram:ApplicableHeaderTradeDelivery'); // Tag muss vorhanden sein

                if (not empty(qRech.A2C('a_vstrasse'))) then begin
                    oXML.OpenElement('ram:ShipToTradeParty'); // Abweichender Warenempfänger
                        oXML.OpenElement('ram:PostalTradeAddress'); // BR-57
                            oXML.AddElement('ram:PostcodeCode', qRech.A2C('a_vplz'));
                            oXML.AddElement('ram:LineOne', qRech.A2C('a_vstrasse'));
                            oXML.AddElement('ram:CityName', qRech.A2C('a_vort'));
                            oXML.AddElement('ram:CountryID', 'DE'); // BR-11
                        oXML.CloseElement; // ram:PostalTradeAddress
                    oXML.CloseElement;
                end;

                oXML.OpenElement('ram:ActualDeliverySupplyChainEvent');
                    oXML.OpenElement('ram:OccurrenceDateTime');
                        oXML.AddAttrElement('udt:DateTimeString','format', '102', DToS(dLiefDat)); // Datum // BT-72
                    oXML.CloseElement;
                oXML.CloseElement;

            oXML.CloseElement;

            oXML.OpenElement('ram:ApplicableHeaderTradeSettlement');
                oXML.AddElement('ram:InvoiceCurrencyCode', 'EUR'); // Währungscode nach ISO 4217 // BR-5

                // BR-49, BR-50, BR-51
                if (not empty(qStamm.A2C('zus_iban1'))) then begin
                    oXML.OpenElement('ram:SpecifiedTradeSettlementPaymentMeans');
                        oXML.AddElement('ram:TypeCode', '30'); // 30 = Überweisung // BR-49
                        oXML.AddElement('ram:Information', qStamm.A2C('zus_bank1'));
                        oXML.OpenElement('ram:PayeePartyCreditorFinancialAccount');
                            oXML.AddElement('ram:IBANID', qStamm.A2C('zus_iban1'));
                        oXML.CloseElement; // ram:PayeePartyCreditorFinancialAccount
                    oXML.CloseElement; // ram:SpecifiedTradeSettlementPaymentMeans
                end;
                if (not empty(qStamm.A2C('zus_iban2'))) then begin
                oXML.OpenElement('ram:SpecifiedTradeSettlementPaymentMeans');
                    oXML.AddElement('ram:TypeCode', '30'); // 30 = Überweisung // BR-49
                    oXML.AddElement('ram:Information', qStamm.A2C('zus_bank2'));
                    oXML.OpenElement('ram:PayeePartyCreditorFinancialAccount');
                        oXML.AddElement('ram:IBANID', qStamm.A2C('zus_iban2'));
                    oXML.CloseElement; // ram:PayeePartyCreditorFinancialAccount
                oXML.CloseElement; // ram:SpecifiedTradeSettlementPaymentMeans
                end;
                if (not empty(qStamm.A2C('zus_iban3'))) then begin
                oXML.OpenElement('ram:SpecifiedTradeSettlementPaymentMeans');
                    oXML.AddElement('ram:TypeCode', '30'); // 30 = Überweisung // BR-49
                    oXML.AddElement('ram:Information', qStamm.A2C('zus_bank3'));
                    oXML.OpenElement('ram:PayeePartyCreditorFinancialAccount');
                        oXML.AddElement('ram:IBANID', qStamm.A2C('zus_iban3'));
                    oXML.CloseElement; // ram:PayeePartyCreditorFinancialAccount
                oXML.CloseElement; // ram:SpecifiedTradeSettlementPaymentMeans
                end;
                if (not empty(qStamm.A2C('zus_iban4'))) then begin
                oXML.OpenElement('ram:SpecifiedTradeSettlementPaymentMeans');
                    oXML.AddElement('ram:TypeCode', '30'); // 30 = Überweisung // BR-49
                    oXML.AddElement('ram:Information', qStamm.A2C('zus_bank4'));
                    oXML.OpenElement('ram:PayeePartyCreditorFinancialAccount');
                        oXML.AddElement('ram:IBANID', qStamm.A2C('zus_iban4'));
                    oXML.CloseElement; // ram:PayeePartyCreditorFinancialAccount
                oXML.CloseElement; // ram:SpecifiedTradeSettlementPaymentMeans
                end;
                if (not empty(qStamm.A2C('zus_iban5'))) then begin
                oXML.OpenElement('ram:SpecifiedTradeSettlementPaymentMeans');
                    oXML.AddElement('ram:TypeCode', '30'); // 30 = Überweisung // BR-49
                    oXML.AddElement('ram:Information', qStamm.A2C('zus_bank5'));
                    oXML.OpenElement('ram:PayeePartyCreditorFinancialAccount');
                        oXML.AddElement('ram:IBANID', qStamm.A2C('zus_iban5'));
                    oXML.CloseElement; // ram:PayeePartyCreditorFinancialAccount
                oXML.CloseElement; // ram:SpecifiedTradeSettlementPaymentMeans
                end;

                nMWSTTotal := 0;
                if (not empty(qRech.A2C('a_mwstschl1'))) then begin
                oXML.OpenElement('ram:ApplicableTradeTax');
                    nMWST := qRech.A2F('a_net1') * (qRech.A2F('a_mwst1') / 100);
                    nMWSTTotal := nMWSTTotal + nMWST;
                    oXML.AddElement('ram:CalculatedAmount',_Format(nMWST)); // BR-46
                    oXML.AddElement('ram:TypeCode', 'VAT');
                    oXML.AddElement('ram:BasisAmount', _Format(qRech.A2F('a_net1'))); // BR-45
                    oXML.AddElement('ram:CategoryCode', OBSMwstSchl_To_ZUGCategoryCode(qRech.A2C('a_art'))); // BR-47
                    oXML.AddElement('ram:RateApplicablePercent', _Format(qRech.A2F('a_mwst1'))); // BR-48
                oXML.CloseElement; // ram:ApplicableTradeTax
                end;
                if (not empty(qRech.A2C('a_mwstschl2'))) then begin
                oXML.OpenElement('ram:ApplicableTradeTax');
                    nMWST := qRech.A2F('a_net2') * (qRech.A2F('a_mwst2') / 100);
                    nMWSTTotal := nMWSTTotal + nMWST;
                    oXML.AddElement('ram:CalculatedAmount',_Format(nMWST)); // BR-46
                    oXML.AddElement('ram:TypeCode', 'VAT');
                    oXML.AddElement('ram:BasisAmount', _Format(qRech.A2F('a_net2'))); // BR-45
                    oXML.AddElement('ram:CategoryCode', OBSMwstSchl_To_ZUGCategoryCode(qRech.A2C('a_art'))); // BR-47
                    oXML.AddElement('ram:RateApplicablePercent', _Format(qRech.A2F('a_mwst2'))); // BR-48
                oXML.CloseElement; // ram:ApplicableTradeTax
                end;
                if (not empty(qRech.A2C('a_mwstschl3'))) then begin
                oXML.OpenElement('ram:ApplicableTradeTax');
                    nMWST := qRech.A2F('a_net3') * (qRech.A2F('a_mwst3') / 100);
                    nMWSTTotal := nMWSTTotal + nMWST;
                    oXML.AddElement('ram:CalculatedAmount',_Format(nMWST)); // BR-46
                    oXML.AddElement('ram:TypeCode', 'VAT');
                    oXML.AddElement('ram:BasisAmount', _Format(qRech.A2F('a_net3'))); // BR-45
                    oXML.AddElement('ram:CategoryCode', OBSMwstSchl_To_ZUGCategoryCode(qRech.A2C('a_art'))); // BR-47
                    oXML.AddElement('ram:RateApplicablePercent', _Format(qRech.A2F('a_mwst3'))); // BR-48
                oXML.CloseElement; // ram:ApplicableTradeTax
                end;

                oXML.OpenElement('ram:SpecifiedTradePaymentTerms');
                    // Fälligkeitsdatum
                    oXML.OpenElement('ram:DueDateDateTime');
                        oXML.AddAttrElement('udt:DateTimeString','format', '102', DToS(qRech.A2D('a_faellig'))); // Datum // BT-9, BR-CO-25
                    oXML.CloseElement; // ram:DueDateDateTime
                oXML.CloseElement; // ram:SpecifiedTradePaymentTerms

                oXML.OpenElement('ram:SpecifiedTradeSettlementHeaderMonetarySummation');
                    oXML.AddElement('ram:LineTotalAmount', _Format(qRech.A2F('a_nbetrag'))); // BR-12
                    oXML.AddElement('ram:TaxBasisTotalAmount', _Format(qRech.A2F('a_nbetrag'))); // BR-13
                    oXML.AddAttrElement('ram:TaxTotalAmount','currencyID','EUR',_Format(nMWSTTotal)); // BR-53
                    oXML.AddElement('ram:GrandTotalAmount', _Format(qRech.A2F('a_bbetrag'))); // BR-14
                    oXML.AddElement('ram:DuePayableAmount', _Format(qRech.A2F('a_bbetrag'))); // BR-15
                oXML.CloseElement(); // ram:SpecifiedTradeSettlementHeaderMonetarySummation

            oXML.CloseElement; // ram:ApplicableHeaderTradeSettlement

        oXML.CloseElement; // rsm:SupplyChainTradeTransaction

    oXML.CloseElement; // rsm:CrossIndustryInvoice
    
end;