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

This commit is contained in:
Sebastian Sieber
2021-01-13 08:41:11 +01:00
14 changed files with 338 additions and 130 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [jstaerk] custom: ["https://www.mustangproject.org/support/"]

View File

@@ -1,3 +1,9 @@
2.1.0
=======
- fixed a charge/allowance rounding error #212
- Corrected intra community supply tax exemption category code
2.0.3 2.0.3
======= =======
2020-12-06 2020-12-06

View File

@@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>Mustang-CLI</artifactId> <artifactId>Mustang-CLI</artifactId>
<name>Mustang commandline tool</name> <name>e-invoices commandline tool, allowing to create(embed), split and validate Factur-X/ZUGFeRD files. Validation should also work for XRechnung/CII. </name>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>2.0.4-SNAPSHOT</version> <version>2.0.4-SNAPSHOT</version>
<repositories> <repositories>
@@ -53,7 +53,6 @@
<version>4.13.1</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- for directory validation: --> <!-- for directory validation: -->
<dependency> <dependency>
<groupId>org.xmlunit</groupId> <groupId>org.xmlunit</groupId>

View File

@@ -11,8 +11,8 @@
<artifactId>library</artifactId> <artifactId>library</artifactId>
<version>2.0.4-SNAPSHOT</version> <version>2.0.4-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Library to write and read FacturX and ZUGFeRD e-invoices. </name> <name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD and to a limited extend XRechnung/CII). </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. <description>The Mustang project is a java library to read, write and validate Factur-X/ZUGFeRD meta data inside your invoice PDFs. To write files, a provided PDF/A will be combined with generated or provided XML.
</description> </description>
<url>http://www.mustangproject.org/</url> <url>http://www.mustangproject.org/</url>
<scm> <scm>
@@ -83,6 +83,14 @@
<version>4.13.1</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- CII to UBL conversion -->
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>1.2.5</version>
</dependency>
<dependency> <dependency>
<groupId>com.helger</groupId> <groupId>com.helger</groupId>
<artifactId>ph-schematron</artifactId> <artifactId>ph-schematron</artifactId>
@@ -101,6 +109,12 @@
<artifactId>xmlunit-assertj</artifactId> <artifactId>xmlunit-assertj</artifactId>
<version>2.6.3</version> <version>2.6.3</version>
</dependency> </dependency>
<dependency>
<groupId>com.helger</groupId>
<artifactId>ph-commons</artifactId>
<version>9.1.1</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -0,0 +1,50 @@
package org.mustangproject.CII;
import com.helger.commons.error.list.ErrorList;
import com.helger.en16931.cii2ubl.CIIToUBL22Converter;
import com.helger.ubl21.UBL21Writer;
import com.helger.ubl22.UBL22Writer;
import java.io.File;
import java.io.Serializable;
public class CIIToUBL {
/***
* converts a CII XML file to a UBL XML file
* thanks to Philip Helger for his library
* @param input
* @param output
*/
public void convert(File input, File output) {
ErrorList occurred=new ErrorList();
CIIToUBL22Converter cc=new CIIToUBL22Converter();
Serializable aUBL = cc.convertCIItoUBL(input, occurred);
if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_21.InvoiceType)
{
UBL21Writer.invoice ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.invoice_21.InvoiceType) aUBL, output);
}
else
if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_21.CreditNoteType)
{
UBL21Writer.creditNote ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.creditnote_21.CreditNoteType) aUBL, output);
}
else
if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_22.InvoiceType)
{
UBL22Writer.invoice ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.invoice_22.InvoiceType) aUBL, output);
}
else
if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_22.CreditNoteType)
{
UBL22Writer.creditNote ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.creditnote_22.CreditNoteType) aUBL, output);
}
}
}

View File

@@ -126,13 +126,19 @@ public interface IExportableTransaction {
return null; return null;
} }
IZUGFeRDAllowanceCharge[] getZFAllowances(); default IZUGFeRDAllowanceCharge[] getZFAllowances() {
return null;
}
IZUGFeRDAllowanceCharge[] getZFCharges(); default IZUGFeRDAllowanceCharge[] getZFCharges() {
return null;
}
IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges(); default IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return null;
}
IZUGFeRDExportableItem[] getZFItems(); IZUGFeRDExportableItem[] getZFItems();
@@ -290,7 +296,7 @@ public interface IExportableTransaction {
* @return three character currency of this invoice * @return three character currency of this invoice
*/ */
default String getCurrency() { default String getCurrency() {
return null; return "EUR";
} }

View File

@@ -37,13 +37,17 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
* item level discounts * item level discounts
* @return array of the discounts on a single item * @return array of the discounts on a single item
*/ */
IZUGFeRDAllowanceCharge[] getItemAllowances(); default IZUGFeRDAllowanceCharge[] getItemAllowances() {
return null;
}
/** /**
* item level price additions * item level price additions
* @return array of the additional charges on the item * @return array of the additional charges on the item
*/ */
IZUGFeRDAllowanceCharge[] getItemCharges(); default IZUGFeRDAllowanceCharge[] getItemCharges() {
return null;
}
/** /**

View File

@@ -68,17 +68,13 @@ public interface IZUGFeRDExportableTradeParty {
* *
* @return First and last name of the recipient * @return First and last name of the recipient
*/ */
default String getName() { String getName();
return null;
}
/** /**
* Postal code of the recipient * Postal code of the recipient
* *
* @return Postal code of the recipient * @return Postal code of the recipient
*/ */
default String getZIP() { String getZIP();
return null;
}
/** /**
@@ -96,9 +92,7 @@ public interface IZUGFeRDExportableTradeParty {
* *
* @return two-letter iso country code of the contact * @return two-letter iso country code of the contact
*/ */
default String getCountry() { String getCountry();
return null;
}
/** /**
@@ -106,9 +100,7 @@ public interface IZUGFeRDExportableTradeParty {
* *
* @return Returns the city of the recipient * @return Returns the city of the recipient
*/ */
default String getLocation() { String getLocation();
return null;
}
/** /**
@@ -116,9 +108,7 @@ public interface IZUGFeRDExportableTradeParty {
* *
* @return street address (street+number) of the contact * @return street address (street+number) of the contact
*/ */
default String getStreet() { String getStreet();
return null;
}
/** /**
* returns additional address information which is display in xml tag "LineTwo" * returns additional address information which is display in xml tag "LineTwo"

View File

@@ -20,7 +20,6 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.time.Year;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -32,7 +31,7 @@ public class Profiles {
{"BASIC", new Profile("BASIC", "urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic")}, {"BASIC", new Profile("BASIC", "urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic")},
{"EN16931", new Profile("EN16931", "urn:cen.eu:en16931:2017")}, {"EN16931", new Profile("EN16931", "urn:cen.eu:en16931:2017")},
{"EXTENDED", new Profile("EXTENDED", "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended")}, {"EXTENDED", new Profile("EXTENDED", "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended")},
{"XRECHNUNG", new Profile("XRECHNUNG", "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_"+((Year.now().getValue()<=2020)?"1.2":"2.0"))} //intentionally switch to XRechnung 2.0 on 01.01.2021 {"XRECHNUNG", new Profile("XRECHNUNG", "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.0")} //switched to XRechnung 2.0 in 2021
}).collect(Collectors.toMap(data -> (String) data[0], data -> (Profile) data[1])); }).collect(Collectors.toMap(data -> (String) data[0], data -> (Profile) data[1]));
static Map<String, Profile> zf1Map = Stream.of(new Object[][]{ static Map<String, Profile> zf1Map = Stream.of(new Object[][]{

View File

@@ -19,7 +19,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
* @param trans the invoice (or IExportableTransaction) to be calculated * @param trans the invoice (or IExportableTransaction) to be calculated
*/ */
public TransactionCalculator(IExportableTransaction trans) { public TransactionCalculator(IExportableTransaction trans) {
this.trans=trans; this.trans = trans;
} }
/*** /***
@@ -30,7 +30,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
if (trans.getTotalPrepaidAmount() == null) { if (trans.getTotalPrepaidAmount() == null) {
return BigDecimal.ZERO; return BigDecimal.ZERO;
} else { } else {
return trans.getTotalPrepaidAmount().setScale(2, RoundingMode.HALF_UP); return trans.getTotalPrepaidAmount().setScale(2, RoundingMode.HALF_UP);
} }
} }
@@ -42,9 +42,9 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
final BigDecimal res = getTaxBasis(); final BigDecimal res = getTaxBasis();
return getVATPercentAmountMap().values().stream() return getVATPercentAmountMap().values().stream()
.map(VATAmount::getCalculated) .map(VATAmount::getCalculated)
.map(p -> p.setScale(2, RoundingMode.HALF_UP)) .map(p -> p.setScale(2, RoundingMode.HALF_UP))
.reduce(BigDecimal.ZERO, BigDecimal::add).add(res); .reduce(BigDecimal.ZERO, BigDecimal::add).add(res);
} }
/*** /***
@@ -58,15 +58,15 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
} }
private BigDecimal sumAllowanceCharge(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) { private BigDecimal sumAllowanceCharge(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) {
BigDecimal res = BigDecimal.ZERO; BigDecimal res = BigDecimal.ZERO;
if ((charges != null) && (charges.length > 0)) { if ((charges != null) && (charges.length > 0)) {
for (IZUGFeRDAllowanceCharge currentCharge : charges) { for (IZUGFeRDAllowanceCharge currentCharge : charges) {
if ((percent==null)||(currentCharge.getTaxPercent().compareTo(percent)==0)) { if ((percent == null) || (currentCharge.getTaxPercent().compareTo(percent) == 0)) {
res = res.add(currentCharge.getTotalAmount(this)); res = res.add(currentCharge.getTotalAmount(this));
} }
} }
} }
return res; return res;
} }
/*** /***
@@ -78,23 +78,23 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
IZUGFeRDAllowanceCharge[] charges = trans.getZFCharges(); IZUGFeRDAllowanceCharge[] charges = trans.getZFCharges();
String res = getAllowanceChargeReasonForPercent(percent, charges); String res = getAllowanceChargeReasonForPercent(percent, charges);
if ("".equals(res)) { if ("".equals(res)) {
res="Charges"; res = "Charges";
} }
return res; return res;
} }
private String getAllowanceChargeReasonForPercent(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) { private String getAllowanceChargeReasonForPercent(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) {
String res = " "; String res = " ";
if ((charges != null) && (charges.length > 0)) { if ((charges != null) && (charges.length > 0)) {
for (IZUGFeRDAllowanceCharge currentCharge : charges) { for (IZUGFeRDAllowanceCharge currentCharge : charges) {
if ((percent==null)||(currentCharge.getTaxPercent().compareTo(percent)==0) if ((percent == null) || (currentCharge.getTaxPercent().compareTo(percent) == 0)
&& currentCharge.getReason()!=null) { && currentCharge.getReason() != null) {
res += currentCharge.getReason()+" "; res += currentCharge.getReason() + " ";
} }
} }
} }
res=res.substring(0,res.length()-1); res = res.substring(0, res.length() - 1);
return res; return res;
} }
/*** /***
@@ -106,7 +106,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
IZUGFeRDAllowanceCharge[] allowances = trans.getZFAllowances(); IZUGFeRDAllowanceCharge[] allowances = trans.getZFAllowances();
String res = getAllowanceChargeReasonForPercent(percent, allowances); String res = getAllowanceChargeReasonForPercent(percent, allowances);
if ("".equals(res)) { if ("".equals(res)) {
res="Allowances"; res = "Allowances";
} }
return res; return res;
} }
@@ -127,10 +127,11 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
* @return item sum * @return item sum
*/ */
protected BigDecimal getTotal() { protected BigDecimal getTotal() {
return Stream.of(trans.getZFItems()) BigDecimal dec = Stream.of(trans.getZFItems())
.map(LineCalculator::new) .map(LineCalculator::new)
.map(LineCalculator::getItemTotalNetAmount) .map(LineCalculator::getItemTotalNetAmount)
.reduce(ZERO, BigDecimal::add); .reduce(ZERO, BigDecimal::add);
return dec;
} }
/*** /***
@@ -139,8 +140,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
* @return item sum +- charges/allowances * @return item sum +- charges/allowances
*/ */
protected BigDecimal getTaxBasis() { protected BigDecimal getTaxBasis() {
BigDecimal res = getTotal().add(getChargesForPercent(null)).subtract(getAllowancesForPercent(null)); return getTotal().add(getChargesForPercent(null).setScale(2, RoundingMode.HALF_UP)).subtract(getAllowancesForPercent(null).setScale(2, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
return res.setScale(2, RoundingMode.HALF_UP);
} }
/** /**
@@ -172,8 +172,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
for (IZUGFeRDAllowanceCharge currentCharge : charges) { for (IZUGFeRDAllowanceCharge currentCharge : charges) {
VATAmount theAmount = hm.get(currentCharge.getTaxPercent().stripTrailingZeros()); VATAmount theAmount = hm.get(currentCharge.getTaxPercent().stripTrailingZeros());
if (theAmount == null) { if (theAmount == null) {
theAmount = new VATAmount(BigDecimal.ZERO, BigDecimal.ZERO, theAmount = new VATAmount(BigDecimal.ZERO, BigDecimal.ZERO,
currentCharge.getCategoryCode()!=null?currentCharge.getCategoryCode():"S"); currentCharge.getCategoryCode() != null ? currentCharge.getCategoryCode() : "S");
} }
theAmount.setBasis(theAmount.getBasis().add(currentCharge.getTotalAmount(this))); theAmount.setBasis(theAmount.getBasis().add(currentCharge.getTotalAmount(this)));
BigDecimal factor = currentCharge.getTaxPercent().divide(new BigDecimal(100)); BigDecimal factor = currentCharge.getTaxPercent().divide(new BigDecimal(100));
@@ -186,8 +186,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
for (IZUGFeRDAllowanceCharge currentAllowance : allowances) { for (IZUGFeRDAllowanceCharge currentAllowance : allowances) {
VATAmount theAmount = hm.get(currentAllowance.getTaxPercent().stripTrailingZeros()); VATAmount theAmount = hm.get(currentAllowance.getTaxPercent().stripTrailingZeros());
if (theAmount == null) { if (theAmount == null) {
theAmount = new VATAmount(BigDecimal.ZERO, BigDecimal.ZERO, theAmount = new VATAmount(BigDecimal.ZERO, BigDecimal.ZERO,
currentAllowance.getCategoryCode()!=null?currentAllowance.getCategoryCode():"S"); currentAllowance.getCategoryCode() != null ? currentAllowance.getCategoryCode() : "S");
} }
theAmount.setBasis(theAmount.getBasis().subtract(currentAllowance.getTotalAmount(this))); theAmount.setBasis(theAmount.getBasis().subtract(currentAllowance.getTotalAmount(this)));
BigDecimal factor = currentAllowance.getTaxPercent().divide(new BigDecimal(100)); BigDecimal factor = currentAllowance.getTaxPercent().divide(new BigDecimal(100));

View File

@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
/** /**
* Mustangproject's ZUGFeRD implementation * Mustangproject's ZUGFeRD implementation
@@ -55,7 +56,7 @@ public class VATAmount {
} }
public void setBasis(BigDecimal basis) { public void setBasis(BigDecimal basis) {
this.basis = basis; this.basis = basis.setScale(2, RoundingMode.HALF_UP);
} }

View File

@@ -24,5 +24,5 @@ public class TaxCategoryCodeTypeConstants {
public static final String TAXEXEMPT = "E"; public static final String TAXEXEMPT = "E";
public static final String ZEROTAXPRODUCTS = "Z"; public static final String ZEROTAXPRODUCTS = "Z";
public static final String UNTAXEDSERVICE = "O"; public static final String UNTAXEDSERVICE = "O";
public static final String INTRACOMMUNITY = "IC"; public static final String INTRACOMMUNITY = "K";
} }

View File

@@ -0,0 +1,202 @@
package org.mustangproject.ZUGFeRD;
import static java.math.BigDecimal.TEN;
import static java.math.BigDecimal.valueOf;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.mustangproject.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
/***
* tests the linecalculator and transactioncalculator classes
*
*/
public class CalculationTest {
@Test
public void testLineCalculator_simpleAmounts_resultInValidVATAmount() {
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(100))
.setQuantity(TEN)
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(100).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1000).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(160).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorInclusiveAllowance() {
//This test failed with previous implementation. By rounding the totalVATAmount to 2 decimal places the result became wrong
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
// 10 % discount on each item
final IZUGFeRDAllowanceCharge allowance = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(14.8730));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(148.73))
.setQuantity(valueOf(12))
.setItemAllowances(new IZUGFeRDAllowanceCharge[]{allowance})
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(133.857).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1606.28).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(257.0048).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorInclusiveAllowanceAndCharge() {
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
// 10 % discount on each item
final IZUGFeRDAllowanceCharge allowance = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(14.873));
// 20 % charge
final IZUGFeRDAllowanceCharge charge = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(29.746));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(148.73))
.setQuantity(valueOf(12))
.setItemAllowances(new IZUGFeRDAllowanceCharge[]{allowance})
.setItemCharges(new IZUGFeRDAllowanceCharge[]{charge})
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(163.603).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1963.24).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(314.1184).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testTotalCalculatorGrandTotalRounding() {
SimpleDateFormat sqlDate = new SimpleDateFormat("yyyy-MM-dd");
BigDecimal sales_tax_percent1 = new BigDecimal(16);
BigDecimal total_increase_percent = new BigDecimal(0.80);
BigDecimal total_discount_percent = new BigDecimal(2.00);
/* invoice (1st part) */
Invoice invoice = new Invoice();
invoice.setDocumentName("Rechnung");
invoice.setNumber("777777");
try {
invoice.setIssueDate(sqlDate.parse("2020-12-31"));
invoice.setDetailedDeliveryPeriod(sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[0]), sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[1]));
invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) {
Logger.getLogger(CalculationTest.class.getName()).log(Level.SEVERE, null, e);
}
/* trade party (sender) */
TradeParty sender = new TradeParty("Maier GmbH", "Musterweg 5", "11111", "Testung", "DE");
sender.addVATID("DE2222222222");
invoice.setSender(sender);
/* trade party (recipient) */
TradeParty recipient = new TradeParty("Teston GmbH" + " " + "Zentrale" + " " + "", "Testweg 5", "11111", "Testung", "DE");
recipient.setID("111111");
recipient.addVATID("DE111111111");
invoice.setRecipient(recipient);
/* item */
Product product;
Item item;
BigDecimal item_increase = BigDecimal.ZERO;
BigDecimal item_discount = BigDecimal.ZERO;
product = new Product("AAA", "", "H84", sales_tax_percent1).setSellerAssignedID("1AAA");
item = new Item(product, new BigDecimal("4.750"), new BigDecimal(5.00));
item_discount = new BigDecimal("10.00");
if (item_increase.compareTo(BigDecimal.ZERO) > 0) {
item.addCharge(new Charge().setPercent(item_increase).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschlag"));
}
if (item_discount.compareTo(BigDecimal.ZERO) > 0) {
item.addAllowance(new Allowance().setPercent(item_discount).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatt"));
}
invoice.addItem(item);
product = new Product("BBB", "", "H84", sales_tax_percent1).setSellerAssignedID("2BBB");
item = new Item(product, new BigDecimal("5.750"), new BigDecimal(4.00));
item_discount = BigDecimal.ZERO;
if (item_increase.compareTo(BigDecimal.ZERO) > 0) {
item.addCharge(new Charge().setPercent(item_increase).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschlag"));
}
if (item_discount.compareTo(BigDecimal.ZERO) > 0) {
item.addAllowance(new Allowance().setPercent(item_discount).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatt"));
}
invoice.addItem(item);
product = new Product("CCC", "", "H84", sales_tax_percent1).setSellerAssignedID("3CCC");
item = new Item(product, new BigDecimal("6.750"), new BigDecimal(3.00));
item_discount = new BigDecimal("10.00");
if (item_increase.compareTo(BigDecimal.ZERO) > 0) {
item.addCharge(new Charge().setPercent(item_increase).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschlag"));
}
if (item_discount.compareTo(BigDecimal.ZERO) > 0) {
item.addAllowance(new Allowance().setPercent(item_discount).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatt"));
}
invoice.addItem(item);
product = new Product("DDD", "", "H84", sales_tax_percent1).setSellerAssignedID("4DDD");
item = new Item(product, new BigDecimal("7.750"), new BigDecimal(2.00));
item_discount = BigDecimal.ZERO;
if (item_increase.compareTo(BigDecimal.ZERO) > 0) {
item.addCharge(new Charge().setPercent(item_increase).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschlag"));
}
if (item_discount.compareTo(BigDecimal.ZERO) > 0) {
item.addAllowance(new Allowance().setPercent(item_discount).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatt"));
}
invoice.addItem(item);
product = new Product("EEE", "", "H84", sales_tax_percent1).setSellerAssignedID("5EEE");
item = new Item(product, new BigDecimal("8.750"), new BigDecimal(1.00));
item_discount = BigDecimal.ZERO;
if (item_increase.compareTo(BigDecimal.ZERO) > 0) {
item.addCharge(new Charge().setPercent(item_increase).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschlag"));
}
if (item_discount.compareTo(BigDecimal.ZERO) > 0) {
item.addAllowance(new Allowance().setPercent(item_discount).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatt"));
}
invoice.addItem(item);
if (total_increase_percent.compareTo(BigDecimal.ZERO) > 0) {
invoice.addCharge(new Charge().setPercent(total_increase_percent).setTaxPercent(sales_tax_percent1).setCategoryCode("ZZZ").setReason("Zuschläge"));
}
if (total_discount_percent.compareTo(BigDecimal.ZERO) > 0) {
invoice.addAllowance(new Allowance().setPercent(total_discount_percent).setTaxPercent(sales_tax_percent1).setCategoryCode("95").setReason("Rabatte"));
}
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(valueOf(99.54).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
}
}

View File

@@ -1,63 +0,0 @@
package org.mustangproject.ZUGFeRD;
import static java.math.BigDecimal.TEN;
import static java.math.BigDecimal.valueOf;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class LineCalculatorTest {
@Test
public void testLineCalculator_simpleAmounts_resultInValidVATAmount() {
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(100))
.setQuantity(TEN)
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(100).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1000).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(160).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorInclusiveAllowance() {
//This test failed with previous implementation. By rounding the totalVATAmount to 2 decimal places the result became wrong
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
// 10 % discount on each item
final IZUGFeRDAllowanceCharge allowance = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(14.8730));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(148.73))
.setQuantity(valueOf(12))
.setItemAllowances(new IZUGFeRDAllowanceCharge[] { allowance })
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(133.857).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1606.28).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(257.0048).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorInclusiveAllowanceAndCharge() {
final IZUGFeRDExportableProduct product = new IZUGFeRDExportableProductImpl().setVatPercent(valueOf(16));
// 10 % discount on each item
final IZUGFeRDAllowanceCharge allowance = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(14.873));
// 20 % charge
final IZUGFeRDAllowanceCharge charge = new IZUGFeRDAllowanceChargeImpl().setTotalAmount(valueOf(29.746));
final IZUGFeRDExportableItem currentItem = new IZUGFeRDExportableItemImpl().setPrice(valueOf(148.73))
.setQuantity(valueOf(12))
.setItemAllowances(new IZUGFeRDAllowanceCharge[] { allowance })
.setItemCharges(new IZUGFeRDAllowanceCharge[] { charge })
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(163.603).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1963.24).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(314.1184).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
}