OBS/Makros und Scripting/Anwendungsbereiche/ZUGFERD

Aus OBS Wiki
Version vom 10. Dezember 2021, 15:43 Uhr von Schinke (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „{{Makros und Scripting}} Hier finden Sie eine Übersicht zu Makros und Funktionen die für die Artikelpflege gedacht sind. =ZUGFERD= Es gibt die Möglichkeit d…“)
Zur Navigation springen Zur Suche springen


Hier finden Sie eine Übersicht zu Makros und Funktionen die für die Artikelpflege gedacht sind.

ZUGFERD

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

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.

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;

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;