|
Antwort |
Registriert seit: 9. Okt 2003
WPXOrder ist eine Sammlung von Delphi-Units zur Erstellung und Auswertung von X-Factur (ZUGFeRD) XML-Daten.
Diese Library ist eine möglichst komplette Repräsentierung des ZUGFeRD XSD Schemas als Delphi Klassen. Sie wurde erstellt, um Rechnungs Daten zu laden, zu verarbeiten und zu speichern. Es beinhaltet noch nicht den E-Invoice-Standard und ist darauf ausgerichtet, XML-Daten zu erstellen, die in PDF-Rechnungen eingebettet werden. Um solche Rechnungen zu erstellen, können Sie die praktische PDF-Erstellungs-VCL wPDF verwenden. Um solche Rechnungen zu lesen, können Sie WPViewPDF PLUS verwenden, das nicht nur X-Factur Daten extrahieren, sondern auch anhängen kann. Bitte schauen Sie sich den Quellcode auf unserer WPXOrder GitHub Seite an. Zweck und Lizenzierung WPXOrder wurde entwickelt, um XML-Anhänge für Rechnungen zu erstellen, die als PDFs verteilt werden. Es wurde in Delphi 10.1 entwickelt und funktioniert am besten mit unseren Produkten wPDF oder WPViewPDF PLUS. Es kann auch verwendet werden, um solche XML-Daten zu lesen und auf alle Eigenschaften einfach zuzugreifen. Es kann auch zur Überprüfung der Berechnung (Gesamtsummen) verwendet werden - bitte beachten Sie jedoch, dass wir nicht garantieren können, dass solche Berechnungen in jedem Fall korrekt funktionieren. Durch die Art der Implementierung werden folgende Fehler bei der Erstellung von XML Daten: 1) Schreibfehler der XML namen 2) Eigenschaften in falschem Eltern XML Element 3) Falsche Reihenfolge der Elemente (Wichtig für die Validierung) Desweitern kann man Werte wie TDateTime zuweisen. Mit der mitgelieferten EXE können Sie die Beispiel-PDFs öffnen und die internen Berechnungen mit den in den PDFs hinterlegten Zahlen vergleichen. Mit nur einem Klick können Sie Delphi-Code erstellen, der die Daten so erzeugt, wie sie in die Rechnung geladen werden. Dies wird Ihnen sehr helfen, das XML-Format besser zu verstehen und Ihre Rechnungserstellungssoftware zu konvertieren. Sie können die Komponente ohne Lizenzkosten verwenden, wenn Sie sie in ein Open-Source-Projekt einbinden, das unter der GNU-Lizenz vertrieben wird - mit Ausnahme von Komponenten jeglicher Art oder „Forks“. Wenn Sie die Komponente intern oder in einem Closed-Source-Produkt kommerziell nutzen wollen, ist eine kommerzielle Lizenz erforderlich. Diese Lizenz ist zu einem angemessenen Preis pro Unternehmen erhältlich (named license). Die kommerzielle Lizenz beinhaltet auch Support für 60 Tage nach dem Kauf. Bitte beachten Sie, dass wir uns zu rechtlichen und kalkulatorischen Fragen im Zusammenhang mit X-Factur nicht äußern können. Programmier Referenz (alle Klassen): https://www.wpcubed.com/manuals/wpxorder/index.html Beschreibung: https://www.wpcubed.com/pdf/products/xorder/ GitHub: https://github.com/wpcubed/xorder wPDF - PDF Erzeugen: https://www.wpcubed.com/pdf/products/wpdf/ WPViewPDF PLUS - XML aus PDFs extrahieren oder hinzufügen: https://www.wpcubed.com/pdf/products/pdf-edit/ Hinweise: Auf GitHub befindet sich auch eine (signierte!) EXE welche aus PDFs XML extrahieren kann und direkt Delphi code erzeugen kann, der genau einen XML Rechnungsanhang mit den selben Werte erstellt. Der Code verwendet Generics, erfordert also neuere Delphi Versionen. Viel Erfolg, Julian Anhang: Automatisch generierter code der mit WPXOrder das ZUGFeRD Beispiel "Warenrechnung" erzeugt Geändert von jziersch (24. Dez 2024 um 10:29 Uhr) |
Delphi 10.4 Sydney |
#2
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:
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.
{ 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;
Code:
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.
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; Da der Übergabeparameter ein Array ist, kann man hier auch ein unter-unter Element auslesen. So liest dieser Code das Element aus:
Code:
Welches so geschrieben wird:
if Source.ReadElementValue([Integer(TXTradeParty.DefinedTradeContact),
Integer(TXTradeContact.DepartmentName)], val) then DepartmentName := val.ValueStr else DepartmentName := ''; 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. |
Zitat |
|
#3
Hallo Herr Ziersch,
ich mache gerade Versuche mit den WPXOrder Units und es sieht recht gut aus. ein paar kurze Fragen: 1. Mir scheint da liegt ein Bug bei der Codierung der Steuernummer-Codes vor? Sehe ich das richtig? TTaxIDType Funktionen in der WPXFacturTypes.PAS:
Delphi-Quellcode:
2. Der Bestellprozess in Ihrem Shop ist etwas verwirrend.
function TTaxIDType.AttributeGet(Index: Integer;
var AName, AValue: String): Boolean; begin if index = 0 then begin AName := 'schemeID'; if fschemeID=TTaxID.FC_tax_number then // AValue := 'FA' else AValue := 'VA'; <--- FEHLER!!!! AValue := 'FC' else AValue := 'VA'; <--- KORREKT!!! Result := true; end else Result := false; end; function TTaxIDType.AttributeSet(const AName, AValue: String): Boolean; begin if SameText(AName, 'schemeID') then begin if SameText('FC',AValue) then fschemeID := TTaxID.FC_tax_number else if SameText('VA',AValue) then fschemeID := TTaxID.VA_VAT_number else raise ENotExpectedAttributeValue( AName+'='+ AValue); Result := true; end else Result := false; end; https://www.wpcubed.com/pdf/products/xorder/ beschreibt den Preis als "Order commercial license for EURO 199,92 incl. 19% VAT (only available in Germany)" Beim Abschluß des Bestellprozesses im Shop sind es aber 199,92€ zuzüglich MwSt. Vielen Dank |
Zitat |
|
#4
und noch eine Sache ist mir aufgefallen:
Die WPXOrder arbeitet noch nach ZUGferd 2.2, oder? Denn ich kämpfe mit Rundungsdifferenzen (meist nur 0,01€ = 1ct) bei der Summierung von Nettobeträgen, MwSt-Beträgen und dem Bruttopreis. Wenn ich diese Seite richtig lese: https://nexoma.de/zugferd/ dann steht dort als Neuerung zu ZUGFeRD 2.3(.2):
Zitat:
ZUGFeRD 2.3: Die neue Version des Formats entspricht gleichzeitig der Factur-X Version 1.0.07. Sie bringt eine angepasste Validierung im Profil EXTENDED mit sich. Nutzer dieses Profils sollten daher ein sofortiges Update durchführen. Zu den weiteren Neuerungen gehören die Korrektur kleinerer Fehler in allen Profilen, Ergänzungen für das französische B2B-Mandat und die Einführung von Rundungsungenauigkeiten (englisch auch als „Slack“ bezeichnet) im Profil EXTENDED.
Diese Rundungsungenauigkeiten treten zum Beispiel bei ‚ungeraden Preisen‘ auf. Ein Bruttopreis von 9,99 € kann mitunter nicht exakt als Nettopreis dargestellt werden, da bei der Umrechnung durch die Division mit 1,19 (wegen 19 % MwSt.) mathematische Rundungsfehler entstehen. Der resultierende Nettopreis hat dann oft viele Dezimalstellen. Da viele Systeme jedoch nur zwei Dezimalstellen zulassen, kommt es dadurch zu Rundungsdifferenzen. Diese Slack-Berücksichtigung für solche (1ct) Differenzen würde sehr helfen. |
Zitat |
Delphi 10.4 Sydney |
#5
Das ganze basiert auf dem Schema 2.2, das ist richtig. Dies war der neueste Download zum Zeitpunkt als der Code geschrieben wurde.
Ich habe mal die Änderungen nachgeschlagen: "neue Codelisten" müssten in der Tat als angepasste enums im code eingepflegt werden. In allen Fällen kann allerdings anstatt des Enums der code mit SetValueStr gesetzt werden. Nur dieser text wird geschrieben. Ich habe die XSD Quellen verglichen, die neuen XSD sind zwar viel mehr aber haben insgesamt weniger an Inhalt. So enthält AllowanceChargeReasonCodeContentType kaum noch Werte. Im Moment bin ich mir nicht so sicher, ob es sinnvoll ist, die neuen Werte einzupflegen. Hat jemand Hinweise auf die codes die tatsächlich geändert wurden? "korrigierte technische Prüfungen" sind nicht Teil von WPXOrder, insbesondere das Schematron wird nicht verwendet. Zur validierung habe ich diese Seite als sehr hilfreich empfunden: https://portal3.gefeg.com/invoice/page/validation WPXOrder wurde anhand der offiziellen Beispiele getestet und diese tragen die Versionsnummer 2.3.0. Zu den Rundungsdifferenzen - die interne Berechnung habe ich aufgrund Feedbacks von der Validator Seite implementiert. Den code findet man in function TWPXFactur.FinalizeInvoice. Wesentlich ist hier, dass die Berechnung immer gruppiert nach TAX Value (0,7,19...) erfolgt. Das gilt für Items, Allowances und Charges jeweils insgesamt. Mit dem Modus TWPXOrderCalcMode.VATAddRoundedOnLine in CalcMode kann die Steuer auch zeilenweise addiert werden. Die Berechnung ist natürlich optional, das Konzept von WPXOrder erlaubt es jeden Wert einzeln zu setzen. Geändert von jziersch ( 5. Jan 2025 um 12:43 Uhr) |
Zitat |
Delphi 10.4 Sydney |
#6
[QUOTE=motion;1544750]1. Mir scheint da liegt ein Bug bei der Codierung der Steuernummer-Codes vor? Sehe ich das richtig?
Vielen Dank. Das ist in der Tat ein Tippfehler. 2. Der Bestellprozess in Ihrem Shop ist etwas verwirrend. Sie haben recht, das habe ich berichtigt. Stripe addiert offenbar die MwST (was auch korrekt ist) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |