Merge remote-tracking branch 'remotes/origin/master' into Improvements

This commit is contained in:
Sebastian Sieber
2020-11-27 12:53:01 +01:00
14 changed files with 234 additions and 18 deletions

View File

@@ -1,7 +1,17 @@
2.0.3
=======
- #201 correct embedded files in XRechnung
2.0.2
=======
2020-11-25
- #197 file attachments
- Charges/Allowances CategoryCode improvement PR #198 Thanks to weclapp-dev
- support reverse charge
- support intra community supply also in product class, not only interface
2.0.1
=====

View File

@@ -3,14 +3,14 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>Mustang-CLI</artifactId>
<name>Mustang commandline tool</name>
<packaging>jar</packaging>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<repositories>
<repository>
<!-- for jargs -->
@@ -31,7 +31,7 @@
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
-->

View File

@@ -25,7 +25,7 @@ If you setup a Maven project, you can grab the artifacts using
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.0.0</version>
<version>2.0.2</version>
</dependency>
```

View File

@@ -171,7 +171,6 @@ Change to the project directory and run
* clean the release with `mvn release:clean` and prepare the release with
* `mvn release:prepare -DignoreSnapshots=true` and enter the version numbers.
* After that is through you can create a new release via `mvn release:perform -Dmaven.java.skip=True`.This will also update the maven repo.
* Experimental: mvn deploy -Dregistry=https://maven.pkg.github.com/ZUGFeRD -Dtoken=GH_TOKEN
![screenshot](development_documentation_screenshot_release.png "Screenshot Release")

View File

@@ -3,13 +3,13 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Library to write and read FacturX and ZUGFeRD e-invoices. </name>
<description>The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs. To write files, a provided PDF/A will be combined with generated or provided XML.

View File

@@ -40,6 +40,7 @@ public class Invoice implements IExportableTransaction {
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
protected ArrayList<String> notes = null;
protected String contractReferencedDocument = null;
protected ArrayList<FileAttachment> xmlEmbeddedFiles=null;
protected Date detailedDeliveryDateStart = null;
protected Date detailedDeliveryPeriodEnd = null;
@@ -78,6 +79,22 @@ public class Invoice implements IExportableTransaction {
return this;
}
public Invoice embedFileInXML(FileAttachment fa) {
if (xmlEmbeddedFiles == null) {
xmlEmbeddedFiles=new ArrayList<FileAttachment>();
}
xmlEmbeddedFiles.add(fa);
return this;
}
public FileAttachment[] getAdditionalReferencedDocuments() {
if (xmlEmbeddedFiles == null) {
return null;
}
return xmlEmbeddedFiles.toArray(new FileAttachment[0]);
}
@Override
public String getNumber() {

View File

@@ -10,6 +10,8 @@ import java.math.BigDecimal;
public class Product implements IZUGFeRDExportableProduct {
protected String unit, name, description, sellerAssignedID, buyerAssignedID;
protected BigDecimal VATPercent;
protected boolean isReverseCharge=false;
protected boolean isIntraCommunitySupply=false;
/***
* default constructor
@@ -54,6 +56,37 @@ public class Product implements IZUGFeRDExportableProduct {
return this;
}
@Override
public boolean isReverseCharge() {
return isReverseCharge;
}
@Override
public boolean isIntraCommunitySupply() {
return isIntraCommunitySupply;
}
/***
* sets reverse charge(=delivery to outside EU)
* @return fluent setter
*/
public Product setReverseCharge() {
isReverseCharge=true;
setVATPercent(BigDecimal.ZERO);
return this;
}
/***
* sets intra community supply(=delivery outside the country inside the EU)
* @return fluent setter
*/
public Product setIntraCommunitySupply() {
isIntraCommunitySupply=true;
setVATPercent(BigDecimal.ZERO);
return this;
}
@Override
public String getUnit() {
return unit;

View File

@@ -108,10 +108,16 @@ public interface IZUGFeRDExportableProduct {
return false;
}
default boolean isReverseCharge() {
return false;
}
default String getTaxCategoryCode() {
if (isIntraCommunitySupply()) {
return "K";
} else if (getVATPercent().equals(new BigDecimal(0))) {
return "K"; // within europe
} else if (isReverseCharge()) {
return "AE"; // to out of europe...
} else if (getVATPercent().equals(BigDecimal.ZERO)) {
return "Z"; // zero rated goods
} else {
return "S"; // one of the "standard" rates (not neccessarily a default rate, even a deducted VAT is standard calculation)
@@ -119,8 +125,11 @@ public interface IZUGFeRDExportableProduct {
}
default String getTaxExemptionReason() {
if (isIntraCommunitySupply())
if (isIntraCommunitySupply()) {
return "Intra-community supply";
} else if (isReverseCharge()) {
return "Reverse Charge";
}
return null;
}
}

View File

@@ -442,7 +442,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " <ram:TypeCode>916</ram:TypeCode>\n"
+ " <ram:Name>" + f.getDescription() + "</ram:Name>\n"
+ " <ram:AttachmentBinaryObject mimeCode=\"" + f.getMimetype() + "\"\n"
+ " filename=\"" + f.getFilename() + "\">" + documentContent + "\n"
+ " filename=\"" + f.getFilename() + "\">" + documentContent + "</ram:AttachmentBinaryObject>\n"
+ " </ram:AdditionalReferencedDocument>\n";
}
}

View File

@@ -39,6 +39,7 @@ import static org.xmlunit.assertj.XmlAssert.assertThat;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class XRTest extends TestCase {
final String TARGET_XML = "./target/testout-XR.xml";
final String TARGET_EDGE_XML = "./target/testout-XR-Edge.xml";
public void testXRExport() {
// the writing part
@@ -76,5 +77,48 @@ public class XRTest extends TestCase {
}
}
public void testXREdgeExport() {
// the writing part
String orgname = "Test company";
String number = "123";
String amountStr = "1.00";
BigDecimal amount = new BigDecimal(amountStr);
byte[] b = {12, 13};
FileAttachment fe1=new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
FileAttachment fe2=new FileAttachment("two.pdf", "application/pdf", "Alternative", b);
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
.embedFileInXML(fe1).embedFileInXML(fe2);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt()
.isEqualTo(1); //2 errors are OK because there is a known bug
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
.asDouble()
.isEqualTo(1);
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_EDGE_XML));
writer.write(theXML);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -45,6 +45,8 @@ public class ZF2PushTest extends TestCase {
final String TARGET_RELATIVECHARGESALLOWANCESPDF = "./target/testout-ZF2PushRelativeChargesAllowances.pdf";
final String TARGET_ATTACHMENTSPDF = "./target/testout-ZF2PushAttachments.pdf";
final String TARGET_PUSHEDGE = "./target/testout-ZF2PushEdge.pdf";
final String TARGET_INTRACOMMUNITYSUPPLYPDF = "./target/testout-ZF2PushIntraCommunitySupply.pdf";
final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf";
public void testPushExport() {
@@ -117,8 +119,8 @@ public class ZF2PushTest extends TestCase {
.load(SOURCE_PDF)) {
byte[] b = {12, 13};
ze.attachFile("one.pdf", b, "Application/PDF", "Alternative");
ze.attachFile("two.pdf", b, "Application/PDF", "Alternative");
ze.attachFile("one.pdf", b, "application/pdf", "Alternative");
ze.attachFile("two.pdf", b, "application/pdf", "Alternative");
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID)).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
);
@@ -198,6 +200,108 @@ public class ZF2PushTest extends TestCase {
}
public void testIntraCommunitySupplyExport() {
String orgname = "Test company";
String number = "123";
String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors()
.load(SOURCE_PDF)) {
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711")
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")))
.setDeliveryAddress(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816"))
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(2.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
);
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF);
} catch (IOException e) {
fail("IOException should not be raised in testEdgeExport");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_INTRACOMMUNITYSUPPLYPDF);
assertTrue(zi.getUTF8().contains("EUR"));
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
// Reading ZUGFeRD
assertEquals("15.00", zi.getAmount());
assertEquals(zi.getHolder(), orgname);
assertEquals(zi.getForeignReference(), number);
try {
assertEquals(zi.getVersion(), 2);
} catch (Exception e) {
e.printStackTrace();
}
}
public void testReverseChargeExport() {
String orgname = "Test company";
String number = "123";
String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors()
.load(SOURCE_PDF)) {
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(2.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
);
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_REVERSECHARGEPDF);
} catch (IOException e) {
fail("IOException should not be raised in testEdgeExport");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_REVERSECHARGEPDF);
assertTrue(zi.getUTF8().contains("EUR"));
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
// Reading ZUGFeRD
assertEquals("15.00", zi.getAmount());
assertEquals(zi.getHolder(), orgname);
assertEquals(zi.getForeignReference(), number);
try {
assertEquals(zi.getVersion(), 2);
} catch (Exception e) {
e.printStackTrace();
}
}
public void testChargesAllowancesExport() {
String orgname = "Test company";

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Mustang</name>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
@@ -11,7 +11,7 @@
<name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name>
<packaging>jar</packaging>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<repositories>
<repository>
<!-- for jargs -->
@@ -75,7 +75,7 @@
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>

View File

@@ -157,7 +157,7 @@ public class LibraryTest extends ResourceCase {
* automatically test the xrechnung
*/
public void testXRValidation() {
File tempFile = new File("../library/target/testout-XR.xml");
File tempFile = new File("../library/target/testout-XR-Edge.xml");
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
String res = zfv.validate(tempFile.getAbsolutePath());