Merge remote-tracking branch 'remotes/origin/master' into master
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
|
||||
### 2.0.1 todo
|
||||
- confirm that VAT category code switches from S to Z on 0%VAT
|
||||
- 2.1 support kleinunternehmer, reverse charge?
|
||||
- dont show empty tax number field
|
||||
- fail when no bankverbindung?
|
||||
@@ -8,8 +7,14 @@
|
||||
- build xr skonto???
|
||||
- *validator not to XR error on ZF files (only notices)
|
||||
- xmp errors may not show correctly in log
|
||||
### 2.0.1 done
|
||||
- do not list tax numbers for shiptotradeparties
|
||||
- do not expect dueDate for corrected invoices
|
||||
- XR test now includes guideline ID #172
|
||||
- support zero-rated goods
|
||||
- BigDecimal specific refactoring PR #192 Thanks to weclapp-dev
|
||||
- Preserving metadata PR #193 Thanks to mr-stephan
|
||||
- support zero-rated goods: confirm that VAT category code switches from S to Z on 0%VAT
|
||||
|
||||
2.0.0
|
||||
=====
|
||||
2020-11-12
|
||||
|
||||
@@ -1,21 +1,45 @@
|
||||
package org.mustangproject.ZUGFeRD;
|
||||
|
||||
/***
|
||||
* specifies a kind of a XML completeness levels,
|
||||
* e.g. Factur-X has 6 (Minimum, Basic-WL, Basic, EN16931, Extended, and XRechnung),
|
||||
* ZUGFeRD 1 had and Order-X will have three (Basic, Comfort, Extended)
|
||||
* and the XRechnung has two (Standard, Extension)
|
||||
* For the XRechnung at the time being please use Factur-X's Xrechnung
|
||||
*/
|
||||
public class Profile {
|
||||
protected String name, id;
|
||||
|
||||
/***
|
||||
* Contruct
|
||||
* @param name human readable name of the profile, also used as basis to detemine the XMP Name
|
||||
* @param ID XML Guideline ID
|
||||
*/
|
||||
public Profile(String name, String ID) {
|
||||
this.name = name;
|
||||
this.id = ID;
|
||||
}
|
||||
|
||||
/***
|
||||
* gets the name
|
||||
* @return the name of the profile
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/***
|
||||
* get guideline id
|
||||
* @return the XML Guideline ID of the profile
|
||||
*/
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/***
|
||||
* if the profile is embedded in PDF we need RDF metadata
|
||||
* @return the XMP name string of the profile
|
||||
*/
|
||||
public String getXMPName() {
|
||||
if (name.equals("BASICWL")) {
|
||||
return "BASIC WL";
|
||||
|
||||
@@ -184,13 +184,13 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
||||
|
||||
}
|
||||
xml = xml + " <ram:SellerTradeParty>\n";
|
||||
xml += getTradePartyAsXML(trans.getSender(), true);
|
||||
xml += getTradePartyAsXML(trans.getSender(), true, false);
|
||||
xml += " </ram:SellerTradeParty>\n"
|
||||
+ " <ram:BuyerTradeParty>\n";
|
||||
// + " <ID>GE2020211</ID>\n"
|
||||
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
|
||||
|
||||
xml += getTradePartyAsXML(trans.getRecipient(), false);
|
||||
xml += getTradePartyAsXML(trans.getRecipient(), false, false);
|
||||
if ((trans.getOwnVATID() != null) && (trans.getOwnOrganisationName() != null)) {
|
||||
xml = xml + " <ram:SpecifiedTaxRegistration>\n" + " <ram:ID schemeID=\"VA\">"
|
||||
+ XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n"
|
||||
@@ -209,7 +209,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
||||
+ " <ram:ApplicableSupplyChainTradeDelivery>\n";
|
||||
if (this.trans.getDeliveryAddress() != null) {
|
||||
xml += "<ram:ShipToTradeParty>" +
|
||||
getTradePartyAsXML(this.trans.getDeliveryAddress(), false) +
|
||||
getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) +
|
||||
"</ram:ShipToTradeParty>";
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.mustangproject.FileAttachment;
|
||||
import org.mustangproject.XMLTools;
|
||||
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
|
||||
|
||||
public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
|
||||
@@ -105,14 +106,16 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
return profile;
|
||||
}
|
||||
|
||||
// @todo check if the two boolean args can be refactored
|
||||
/***
|
||||
* returns the UN/CEFACT CII XML for companies(tradeparties), which is actually
|
||||
* the same for ZF1 (v 2013b) and ZF2 (v 2016b)
|
||||
* @param party
|
||||
* @param isSender some attributes are allowed only for senders in certain profiles
|
||||
* @param isShipToTradeParty some attributes are allowed only for senders or recipients
|
||||
* @return
|
||||
*/
|
||||
protected String getTradePartyAsXML(IZUGFeRDExportableTradeParty party, boolean isSender) {
|
||||
protected String getTradePartyAsXML(IZUGFeRDExportableTradeParty party, boolean isSender, boolean isShipToTradeParty) {
|
||||
String xml = "";
|
||||
// According EN16931 either GlobalID or seller assigned ID might be present for BuyerTradeParty
|
||||
// and ShipToTradeParty, but not both. Prefer seller assigned ID for now.
|
||||
@@ -164,13 +167,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
+ " <ram:CountryID>" + XMLTools.encodeXML(party.getCountry())
|
||||
+ "</ram:CountryID>\n"
|
||||
+ " </ram:PostalTradeAddress>\n";
|
||||
if (party.getVATID() != null) {
|
||||
if ((party.getVATID() != null)&&(!isShipToTradeParty)) {
|
||||
xml += " <ram:SpecifiedTaxRegistration>\n"
|
||||
+ " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(party.getVATID())
|
||||
+ "</ram:ID>\n"
|
||||
+ " </ram:SpecifiedTaxRegistration>\n";
|
||||
}
|
||||
if (party.getTaxID() != null) {
|
||||
if ((party.getTaxID() != null)&&(!isShipToTradeParty)) {
|
||||
xml += " <ram:SpecifiedTaxRegistration>\n"
|
||||
+ " <ram:ID schemeID=\"FC\">" + XMLTools.encodeXML(party.getTaxID())
|
||||
+ "</ram:ID>\n"
|
||||
@@ -226,7 +229,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
paymentTermsDescription = trans.getPaymentTermDescription();
|
||||
}
|
||||
|
||||
if (paymentTermsDescription == null) {
|
||||
if ((paymentTermsDescription == null)&&(trans.getDocumentCode()!= org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE)) {
|
||||
paymentTermsDescription = "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate());
|
||||
|
||||
}
|
||||
@@ -396,13 +399,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
|
||||
}
|
||||
xml = xml + " <ram:SellerTradeParty>\n"
|
||||
+ getTradePartyAsXML(trans.getSender(), true)
|
||||
+ getTradePartyAsXML(trans.getSender(), true, false)
|
||||
+ " </ram:SellerTradeParty>\n"
|
||||
+ " <ram:BuyerTradeParty>\n";
|
||||
// + " <ID>GE2020211</ID>\n"
|
||||
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
|
||||
|
||||
xml += getTradePartyAsXML(trans.getRecipient(), false);
|
||||
xml += getTradePartyAsXML(trans.getRecipient(), false, false);
|
||||
xml += " </ram:BuyerTradeParty>\n";
|
||||
|
||||
if (trans.getBuyerOrderReferencedDocumentID() != null) {
|
||||
@@ -436,7 +439,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
+ " <ram:ApplicableHeaderTradeDelivery>\n";
|
||||
if (this.trans.getDeliveryAddress() != null) {
|
||||
xml += "<ram:ShipToTradeParty>" +
|
||||
getTradePartyAsXML(this.trans.getDeliveryAddress(), false) +
|
||||
getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) +
|
||||
"</ram:ShipToTradeParty>";
|
||||
}
|
||||
|
||||
@@ -479,6 +482,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (trans.getDocumentCode()== DocumentCodeTypeConstants.CORRECTEDINVOICE) {
|
||||
hasDueDate=false;
|
||||
}
|
||||
|
||||
HashMap<BigDecimal, VATAmount> VATPercentAmountMap = calc.getVATPercentAmountMap();
|
||||
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
|
||||
|
||||
@@ -260,7 +260,8 @@ public class ZF2PushTest extends TestCase {
|
||||
try {
|
||||
ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
|
||||
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).setID("0009845"))
|
||||
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setID("0008734").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE")))
|
||||
.setDeliveryAddress(new TradeParty("just the other side of the street", "teststr.12a", "55232", "Entenhausen", "DE").addVATID("DE47110"))
|
||||
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setID("0008734").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)), price, new BigDecimal(1.0)).addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))))
|
||||
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
|
||||
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
|
||||
@@ -284,7 +285,10 @@ public class ZF2PushTest extends TestCase {
|
||||
assertTrue(zi.getUTF8().contains("0009845"));
|
||||
assertTrue(zi.getUTF8().contains("0008734"));
|
||||
assertTrue(zi.getUTF8().contains("item level 1/1"));
|
||||
assertTrue(zi.getUTF8().contains("DE4711")); // the VAT ID should be there...
|
||||
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
|
||||
assertTrue(zi.getUTF8().contains("document level 2/2"));
|
||||
assertFalse(zi.getUTF8().contains("++49555123456")); // in profile EN16931 contact fax number is not allowed
|
||||
|
||||
}
|
||||
|
||||
@@ -389,8 +393,8 @@ public class ZF2PushTest extends TestCase {
|
||||
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
|
||||
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors()
|
||||
.load(SOURCE_PDF)) {
|
||||
|
||||
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number)
|
||||
// no due date, since we are not expecting money
|
||||
Invoice i = new Invoice().setIssueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number)
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)).setCorrection("0815");
|
||||
|
||||
@@ -135,6 +135,24 @@ public class LibraryTest extends ResourceCase {
|
||||
assertThat(res).valueByXPath("/validation/summary/@status")
|
||||
.isEqualTo("valid");
|
||||
}
|
||||
public void testPDFA3Exporter() {
|
||||
// testout-MustangGnuaccountingBeispielRE-20170509_505newEdge.pdf was a A3 file
|
||||
// already in import (MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf),
|
||||
// so it's important to check that we did not screw anythig up in that scenario
|
||||
|
||||
File tempFile = new File("../library/target/testout-MustangGnuaccountingBeispielRE-20170509_505newEdge.pdf");
|
||||
assertTrue(tempFile.exists());
|
||||
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
|
||||
|
||||
String res = zfv.validate(tempFile.getAbsolutePath());
|
||||
assertThat(res).valueByXPath("/validation/pdf/summary/@status")
|
||||
.isEqualTo("valid");
|
||||
assertThat(res).valueByXPath("/validation/xml/summary/@status")
|
||||
.isEqualTo("valid");
|
||||
assertThat(res).valueByXPath("/validation/summary/@status")
|
||||
.isEqualTo("valid");
|
||||
}
|
||||
|
||||
/**
|
||||
* automatically test the xrechnung
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user