Merge pull request #309 from chris-webert-its4u/issues/308-vat-due-date
Issue 308: define when VAT collection become applicable
This commit is contained in:
@@ -63,6 +63,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
protected String specifiedProcuringProjectID = null;
|
protected String specifiedProcuringProjectID = null;
|
||||||
protected String specifiedProcuringProjectName = null;
|
protected String specifiedProcuringProjectName = null;
|
||||||
protected String despatchAdviceReferencedDocumentID = null;
|
protected String despatchAdviceReferencedDocumentID = null;
|
||||||
|
protected String vatDueDateTypeCode = null;
|
||||||
|
|
||||||
public Invoice() {
|
public Invoice() {
|
||||||
ZFItems = new ArrayList<>();
|
ZFItems = new ArrayList<>();
|
||||||
@@ -703,4 +704,18 @@ public class Invoice implements IExportableTransaction {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVATDueDateTypeCode() {
|
||||||
|
return vatDueDateTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide when the VAT should be collected.
|
||||||
|
* @param vatDueDateTypeCode use EventTimeCodeTypeConstants
|
||||||
|
*/
|
||||||
|
public Invoice setVATDueDateTypeCode(String vatDueDateTypeCode) {
|
||||||
|
this.vatDueDateTypeCode = vatDueDateTypeCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -497,4 +497,8 @@ public interface IExportableTransaction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default String getVATDueDateTypeCode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,13 +164,14 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
|
|||||||
*/
|
*/
|
||||||
protected HashMap<BigDecimal, VATAmount> getVATPercentAmountMap() {
|
protected HashMap<BigDecimal, VATAmount> getVATPercentAmountMap() {
|
||||||
HashMap<BigDecimal, VATAmount> hm = new HashMap<>();
|
HashMap<BigDecimal, VATAmount> hm = new HashMap<>();
|
||||||
|
final String vatDueDateTypeCode = trans.getVATDueDateTypeCode();
|
||||||
|
|
||||||
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
|
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
|
||||||
BigDecimal percent = currentItem.getProduct().getVATPercent();
|
BigDecimal percent = currentItem.getProduct().getVATPercent();
|
||||||
if (percent != null) {
|
if (percent != null) {
|
||||||
LineCalculator lc = new LineCalculator(currentItem);
|
LineCalculator lc = new LineCalculator(currentItem);
|
||||||
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
|
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
|
||||||
currentItem.getProduct().getTaxCategoryCode());
|
currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode);
|
||||||
VATAmount current = hm.get(percent.stripTrailingZeros());
|
VATAmount current = hm.get(percent.stripTrailingZeros());
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
hm.put(percent.stripTrailingZeros(), itemVATAmount);
|
hm.put(percent.stripTrailingZeros(), itemVATAmount);
|
||||||
@@ -188,7 +189,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
|
|||||||
VATAmount theAmount = hm.get(taxPercent.stripTrailingZeros());
|
VATAmount theAmount = hm.get(taxPercent.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",
|
||||||
|
vatDueDateTypeCode);
|
||||||
}
|
}
|
||||||
theAmount.setBasis(theAmount.getBasis().add(currentCharge.getTotalAmount(this)));
|
theAmount.setBasis(theAmount.getBasis().add(currentCharge.getTotalAmount(this)));
|
||||||
BigDecimal factor = taxPercent.divide(new BigDecimal(100));
|
BigDecimal factor = taxPercent.divide(new BigDecimal(100));
|
||||||
@@ -205,7 +207,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
|
|||||||
VATAmount theAmount = hm.get(taxPercent.stripTrailingZeros());
|
VATAmount theAmount = hm.get(taxPercent.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",
|
||||||
|
vatDueDateTypeCode);
|
||||||
}
|
}
|
||||||
theAmount.setBasis(theAmount.getBasis().subtract(currentAllowance.getTotalAmount(this)));
|
theAmount.setBasis(theAmount.getBasis().subtract(currentAllowance.getTotalAmount(this)));
|
||||||
BigDecimal factor = taxPercent.divide(new BigDecimal(100));
|
BigDecimal factor = taxPercent.divide(new BigDecimal(100));
|
||||||
|
|||||||
@@ -37,12 +37,23 @@ public class VATAmount {
|
|||||||
this.basis = basis;
|
this.basis = basis;
|
||||||
this.calculated = calculated;
|
this.calculated = calculated;
|
||||||
this.categoryCode = categoryCode;
|
this.categoryCode = categoryCode;
|
||||||
|
this.dueDateTypeCode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode, String dueDateTypeCode) {
|
||||||
|
super();
|
||||||
|
this.basis = basis;
|
||||||
|
this.calculated = calculated;
|
||||||
|
this.categoryCode = categoryCode;
|
||||||
|
this.dueDateTypeCode = dueDateTypeCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal basis, calculated, applicablePercent;
|
BigDecimal basis, calculated, applicablePercent;
|
||||||
|
|
||||||
String categoryCode;
|
String categoryCode;
|
||||||
|
|
||||||
|
String dueDateTypeCode;
|
||||||
|
|
||||||
public BigDecimal getApplicablePercent() {
|
public BigDecimal getApplicablePercent() {
|
||||||
return applicablePercent;
|
return applicablePercent;
|
||||||
}
|
}
|
||||||
@@ -95,12 +106,20 @@ public class VATAmount {
|
|||||||
this.categoryCode = categoryCode;
|
this.categoryCode = categoryCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDueDateTypeCode() {
|
||||||
|
return dueDateTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDueDateTypeCode(String dueDateTypeCode) {
|
||||||
|
this.dueDateTypeCode = dueDateTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
public VATAmount add(VATAmount v) {
|
public VATAmount add(VATAmount v) {
|
||||||
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode);
|
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode, this.dueDateTypeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VATAmount subtract(VATAmount v) {
|
public VATAmount subtract(VATAmount v) {
|
||||||
return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.categoryCode);
|
return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.categoryCode, this.dueDateTypeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
|
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
final String amountCategoryCode = amount.getCategoryCode();
|
final String amountCategoryCode = amount.getCategoryCode();
|
||||||
|
final String amountDueDateTypeCode = amount.getDueDateTypeCode();
|
||||||
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
|
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
|
||||||
xml += "<ram:ApplicableTradeTax>"
|
xml += "<ram:ApplicableTradeTax>"
|
||||||
+ "<ram:CalculatedAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getCalculated())
|
+ "<ram:CalculatedAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getCalculated())
|
||||||
@@ -279,6 +280,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
+ (displayExemptionReason ? exemptionReason : "")
|
+ (displayExemptionReason ? exemptionReason : "")
|
||||||
+ "<ram:BasisAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
|
+ "<ram:BasisAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
|
||||||
+ "<ram:CategoryCode>" + amount.getCategoryCode() + "</ram:CategoryCode>"
|
+ "<ram:CategoryCode>" + amount.getCategoryCode() + "</ram:CategoryCode>"
|
||||||
|
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")
|
||||||
+ "<ram:ApplicablePercent>" + vatFormat(currentTaxPercent)
|
+ "<ram:ApplicablePercent>" + vatFormat(currentTaxPercent)
|
||||||
+ "</ram:ApplicablePercent></ram:ApplicableTradeTax>";
|
+ "</ram:ApplicablePercent></ram:ApplicableTradeTax>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -618,6 +618,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
|
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
final String amountCategoryCode = amount.getCategoryCode();
|
final String amountCategoryCode = amount.getCategoryCode();
|
||||||
|
final String amountDueDateTypeCode = amount.getDueDateTypeCode();
|
||||||
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
|
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
|
||||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||||
|
|
||||||
@@ -628,6 +629,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
+ (displayExemptionReason ? exemptionReason : "")
|
+ (displayExemptionReason ? exemptionReason : "")
|
||||||
+ "<ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
|
+ "<ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
|
||||||
+ "<ram:CategoryCode>" + amountCategoryCode + "</ram:CategoryCode>"
|
+ "<ram:CategoryCode>" + amountCategoryCode + "</ram:CategoryCode>"
|
||||||
|
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")
|
||||||
+ "<ram:RateApplicablePercent>"
|
+ "<ram:RateApplicablePercent>"
|
||||||
+ vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent></ram:ApplicableTradeTax>";
|
+ vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent></ram:ApplicableTradeTax>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/** **********************************************************************
|
||||||
|
*
|
||||||
|
* Copyright 2018 Jochen Staerk
|
||||||
|
*
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*********************************************************************** */
|
||||||
|
package org.mustangproject.ZUGFeRD.model;
|
||||||
|
|
||||||
|
public class EventTimeCodeTypeConstants {
|
||||||
|
public static final String INVOICE_DATE = "5";
|
||||||
|
public static final String DELIVERY_DATE = "29";
|
||||||
|
public static final String PAYMENT_DATE = "72";
|
||||||
|
}
|
||||||
@@ -33,6 +33,8 @@ import org.junit.FixMethodOrder;
|
|||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants;
|
||||||
import org.xmlunit.builder.Input;
|
import org.xmlunit.builder.Input;
|
||||||
import org.xmlunit.xpath.JAXPXPathEngine;
|
import org.xmlunit.xpath.JAXPXPathEngine;
|
||||||
import org.xmlunit.xpath.XPathEngine;
|
import org.xmlunit.xpath.XPathEngine;
|
||||||
@@ -390,7 +392,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
|
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
|
||||||
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").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)))
|
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
|
||||||
.setDeliveryDate(sdf.parse("2020-11-02")).setOwnVATID("DE0815").setNumber(number)
|
.setDeliveryDate(sdf.parse("2020-11-02")).setOwnVATID("DE0815").setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
|
||||||
);
|
);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -429,6 +431,8 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
|
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
|
||||||
assertTrue(zi.getUTF8().contains("document level 2/2"));
|
assertTrue(zi.getUTF8().contains("document level 2/2"));
|
||||||
assertFalse(zi.getUTF8().contains("++49555123456")); // in profile EN16931 contact fax number is not allowed
|
assertFalse(zi.getUTF8().contains("++49555123456")); // in profile EN16931 contact fax number is not allowed
|
||||||
|
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString()
|
||||||
|
.isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE);
|
||||||
|
|
||||||
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE);
|
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE);
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user