Einzelnen Beitrag anzeigen

jziersch

Registriert seit: 9. Okt 2003
Ort: München
251 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: WPXOrder - X-Factur (ZUGFeRD) XML Daten lesen und erzeugen

  Alt 9. Nov 2024, 13:30
Jetzt gibt es auch ein Beispiel Projekt mit folgenden Features:

1) XML laden und Kontrollrechnung ausführen
2) XML laden um Rechnungsdaten vorzubelegen (Adresse etc)
3) Aus Rechnungsdaten neue XML erzeugen
4) Geladene oder erzeugte Daten als Pascal Quellcode abspeichern

Dies ist der implementation code des Demo Projekts:

Code:
{ TInvoiceCompanyData }
procedure TInvoiceCompanyData.ReadFromStrings(str: TStrings);
begin
  Name := str.Values['Name'];
  PostcodeCode := str.Values['PostcodeCode'];
  Addres := str.Values['Addres'];
  CityName := str.Values['CityName'];
  CountryIDText := str.Values['CountryID'];
  // Optional
  DepartmentName := str.Values['DepartmentName'];
  // Required for Seller
  TAXId := str.Values['TAXId'];
  // Required for EU Sales
  VATID := str.Values['VATID'];
  // Optional: Full text, several lines for Seller!
  ContactInfo := str.Values['ContactInfo'];
  // Optional
  ID := str.Values['ID'];
  GlobalID := str.Values['GlobalID'];
  GlobalIDScheme := str.Values['GlobalIDScheme'];
  // Optional, Reg Nr
  SpecifiedLegalOrganization := str.Values['SpecifiedLegalOrganization'];
end;

procedure TInvoiceCompanyData.WriteToStrings(str: TStrings);
begin
  str.Values['Name'] := Name;
  str.Values['PostcodeCode'] := PostcodeCode;
  str.Values['Addres'] := Addres;
  str.Values['CityName'] := CityName;
  str.Values['CountryID'] := CountryIDText;
  // Optional
  str.Values['DepartmentName'] := DepartmentName;
  // Required for Seller
  str.Values['TAXId'] := TAXId;
  // Required for EU Sales
  str.Values['VATID'] := VATID;
  // Optional: Full text, several lines for Seller!
  str.Values['ContactInfo'] := ContactInfo;
  // Optional
  str.Values['ID'] := ID;
  str.Values['GlobalID'] := GlobalID;
  str.Values['GlobalIDScheme'] := GlobalIDScheme;
  // Optional, Reg Nr
  str.Values['SpecifiedLegalOrganization'] := SpecifiedLegalOrganization;
end;

procedure TInvoiceForm.SaveasDelphiCode1Click(Sender: TObject);
begin
  SaveDialog1.FilterIndex := 1;
  if SaveDialog1.Execute then
     WPXFactur1.SaveToFile(SaveDialog1.FileName, TWPXOrderDumpMode.DelphiCodeCompact);
end;

procedure TInvoiceForm.SaveXML1Click(Sender: TObject);
begin
  SaveDialog1.FilterIndex := 0;
  if SaveDialog1.Execute then
     WPXFactur1.SaveToFile(SaveDialog1.FileName);
end;

procedure TInvoiceForm.btnCalculateClick(Sender: TObject);
begin
   WPXFactur1.VerifySummation;
   outText.Lines := WPXFactur1.Messages;
end;

procedure TInvoiceForm.btnGenerateInvoiceClick(Sender: TObject);
var i : Integer;
begin
   WPXPaymentData.DueDateDateTime := Now;
   WPXPaymentData.TAXCategory := TTaxCategory(taxCombo.ItemIndex);

   WPXFactur1.StartInvoice(
         TDocumentCode.c380_Commercial_invoice,
         WPXPaymentData.DueDateDateTime,
         edREID.Text,
         edREName.Text,
         WPXSeller, WPXBuyer, nil,
         nil, // from Order
         WPXOrderData,
         nil,
         WPXPaymentData
        );

   for I := 1 to ItemGrid.RowCount-1 do
   if ItemGrid.Cells[1,i]<>'' then
   begin
      WPXFactur1.AddSale(
         ItemGrid.Cells[1,i],
         WPXCurrencyStrToFloat( ItemGrid.Cells[2,i] ),
         WPXCurrencyStrToFloat( ItemGrid.Cells[3,i] ),
         WPXCurrencyStrToFloat( ItemGrid.Cells[5,i] ),
         WPXPaymentData.TAXCategory );
   end;

   WPXFactur1.FinalizeInvoice(true,TCurrencyCode.EUR,0,WPXPaymentData);

   outText.Lines.Assign(WPXFactur1.Messages);
end;

procedure TInvoiceForm.FormCreate(Sender: TObject);
begin
  WPXFactur1 := TWPXFactur.Create(Self);

  WPXSeller := TInvoiceCompanyData.Create;
  WPXBuyer := TInvoiceCompanyData.Create;
  WPXShipTo := TInvoiceCompanyData.Create;
  WPXPaymentData := TPaymentData.Create;
  WPXOrderData := TOrderData.Create;

  ReadItems;
end;

procedure TInvoiceForm.FormDestroy(Sender: TObject);
begin
  WPXFactur1.Free;
  WPXSeller.Free;
  WPXBuyer.Free;
  WPXShipTo.Free;
  WPXPaymentData.Free;
  WPXOrderData.Free;
end;

procedure TInvoiceForm.LoadXML1Click(Sender: TObject);
begin
   if OpenDialog1.Execute then
   begin
     WPXFactur1.LoadFromFile(OpenDialog1.FileName);
     WPXFactur1.ReadCompanyData(WPXSeller,TReadCompanyDataMode.Seller);
     WPXFactur1.ReadCompanyData(WPXBuyer,TReadCompanyDataMode.Buyer);
     WPXFactur1.ReadCompanyData(WPXShipTo,TReadCompanyDataMode.ShipTo);
     // WPXFactur1.ReadCompanyData(xxx,TReadCompanyDataMode.Payee);
     WPXSeller.WriteToStrings(valSeller.Strings);
     WPXBuyer.WriteToStrings(valBuyer.Strings);
     ReadItems;
   end;
end;

procedure TInvoiceForm.ReadItems;
var i : Integer;
    item : TSupplyChainTradeItem;
begin
    ItemGrid.RowCount := 0;
    ItemGrid.RowCount := WPXFactur1.Transaction.Items.Count + 4;
    ItemGrid.ColCount := 7;

    ItemGrid.Cells[0,0] := 'LineID';
    ItemGrid.Cells[1,0] := 'Name';
    ItemGrid.Cells[2,0] := 'Net Charge';
    ItemGrid.Cells[3,0] := 'Quantity';
    ItemGrid.Cells[4,0] := 'TAX Category';
    ItemGrid.Cells[5,0] := 'RateApplicablePercent';
    ItemGrid.Cells[6,0] := 'LineTotalAmount';

    for i := 1 to WPXFactur1.Transaction.Items.Count do
    begin
        item := WPXFactur1.Transaction.Items[i-1];
        with item.Line do
        begin
            ItemGrid.Cells[0,i] := AssociatedDocumentLineDocument.LineID.ValueStr;
            ItemGrid.Cells[1,i] := SpecifiedTradeProduct.Name.ValueStr;
            ItemGrid.Cells[2,i] := SpecifiedLineTradeAgreement.NetPriceProductTradePrice.ChargeAmount.ValueStr;
            ItemGrid.Cells[3,i] := SpecifiedLineTradeAgreement.NetPriceProductTradePrice.BasisQuantity.ValueStr;
            if ItemGrid.Cells[3,i]='' then ItemGrid.Cells[3,i] := '1';
            ItemGrid.Cells[4,i] := SpecifiedLineTradeSettlement.ApplicableTradeTax.CategoryCode.ValueStr;
            ItemGrid.Cells[5,i] := SpecifiedLineTradeSettlement.ApplicableTradeTax.RateApplicablePercent.ValueStr;
            ItemGrid.Cells[6,i] := SpecifiedLineTradeSettlement.SpecifiedTradeSettlementLineMonetarySummation.LineTotalAmount.ValueStr;
            // ItemGrid.Cells[7,i] := SpecifiedLineTradeDelivery.BilledQuantity.ValueStr;
            // ItemGrid.Cells[8,i] := SpecifiedLineTradeSettlement.ApplicableTradeTax.TypeCode.ValueStr;

        end;
    end;
end;
In der unit WPXFactur befindet sich der code zum erzeugen und auslesen der XML Daten sowie zur Berechnung. Hier kann man ganz gut sehen, wie XML Daten ausgelesen werden können, insbesondere die Funktion die die Adressen ausliest.

Code:
procedure TCompanyData.AssignFrom(Source: TTradeParty);
var val : TWPXElement;
begin
  if Source<>nil then
  begin
    // This code reads the value of properties but does not create the elements
    // If they are not existing.
    Source.ReadElementValue([Integer(TXTradeParty.Name)], Name);
    if Source.ReadElementValue([Integer(TXTradeParty.DefinedTradeContact),
            Integer(TXTradeContact.DepartmentName)], val) then
         DepartmentName := val.ValueStr else DepartmentName := '';
    // This code first checks the property exists and then read the values
    Source.ReadElementValue([Integer(TXTradeParty.PostalTradeAddress),
            Integer(TXTradeAddress.PostcodeCode)], PostcodeCode);
    Source.ReadElementValue([Integer(TXTradeParty.PostalTradeAddress),
            Integer(TXTradeAddress.LineOne)], Addres);
    Source.ReadElementValue([Integer(TXTradeParty.PostalTradeAddress),
            Integer(TXTradeAddress.CityName)], CityName);
    if Source.ReadElementValue([Integer(TXTradeParty.PostalTradeAddress),
            Integer(TXTradeAddress.CountryID)], val) then
         CountryIDText := val.ValueStr else CountryID := TCountryID.UNDEFINED;
    Source.ReadElementValue([Integer(TXTradeParty.ID)], ID);
    Source.ReadElementValue([Integer(TXTradeParty.GlobalID)], GlobalID);
    if Source.ReadElementValue([Integer(TXTradeParty.SpecifiedTaxRegistration),
            Integer(TXTaxRegistration.ID)], val) then
         VATID := val.ValueStr else VATID := '';
    if Source.ReadElementValue([Integer(TXTradeParty.SpecifiedLegalOrganization),
            Integer(TXLegalOrganization.ID)], val) then
         SpecifiedLegalOrganization := val.ValueStr else SpecifiedLegalOrganization := '';
  end;
end;
Sie verwendet die Methode ReadElementValue welche auf ein Rechnungsobjekt angewendet, benutzt werden kann ein bestimmtes Unterelement auszulesen, ohne dass diese erzeugt wird, wenn es nicht existiert.

Da der Übergabeparameter ein Array ist, kann man hier auch ein unter-unter Element auslesen.

So liest dieser Code das Element aus:

Code:
if Source.ReadElementValue([Integer(TXTradeParty.DefinedTradeContact),
            Integer(TXTradeContact.DepartmentName)], val) then
         DepartmentName := val.ValueStr else DepartmentName := '';
Welches so geschrieben wird:

TTradeParty(Dest).DefinedTradeContact.DepartmentName.SetValue(DepartmentName);

geschrieben wird. Ohne die Verwendung vom ReadElementValue würde das Unterobjekt DefinedTradeContact und DepartmentName automatisch angelegt, wenn man also einfach auf Source.DefinedTradeContact.DepartmentName.ValueStr zugreifen würde.

Ich hoffe dies erklärt etwas, wie die automatische Erzeugung der Eigenschaften funktioniert.

Übrigens: Viele Eigenschaften in der Rechnung können mehrfach auftreten. Mit Aussnahme der Rechnungsposten (lines), welche durch eine eigene Collection verwaltet werden, wird dies durch eine default Array property von ausgewählten Klassen implementiert.

IncludedNote[1].ContentCode...

greift hier auf die erste Notiz zu, IncludedNote[2].ContentCode... auf die zweite usw. Der Zugriff muss in der richtigen, aufsteigenden Reihenfolge erfolgen. Zu auslesen kann man 'Count' abfragen. Zum sequentiellen Hinzufügen ohne index gibt es ListAdd.
Miniaturansicht angehängter Grafiken
wpxorder_demo.png  
WPCubed GmbH
Komponenten für Delphi:
WPTools, wPDF, WPViewPDF
  Mit Zitat antworten Zitat