Issue 308: define when VAT collection become applicable

This commit is contained in:
Chris Webert
2023-03-03 09:53:26 +01:00
parent 54a3242068
commit 04900a51b2
8 changed files with 80 additions and 6 deletions

View File

@@ -63,6 +63,7 @@ public class Invoice implements IExportableTransaction {
protected String specifiedProcuringProjectID = null;
protected String specifiedProcuringProjectName = null;
protected String despatchAdviceReferencedDocumentID = null;
protected String vatDueDateTypeCode = null;
public Invoice() {
ZFItems = new ArrayList<>();
@@ -703,4 +704,18 @@ public class Invoice implements IExportableTransaction {
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;
}
}

View File

@@ -497,4 +497,8 @@ public interface IExportableTransaction {
return null;
}
default String getVATDueDateTypeCode() {
return null;
}
}

View File

@@ -164,13 +164,14 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
*/
protected HashMap<BigDecimal, VATAmount> getVATPercentAmountMap() {
HashMap<BigDecimal, VATAmount> hm = new HashMap<>();
final String vatDueDateTypeCode = trans.getVATDueDateTypeCode();
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent();
if (percent != null) {
LineCalculator lc = new LineCalculator(currentItem);
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
currentItem.getProduct().getTaxCategoryCode());
currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode);
VATAmount current = hm.get(percent.stripTrailingZeros());
if (current == null) {
hm.put(percent.stripTrailingZeros(), itemVATAmount);
@@ -188,7 +189,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
VATAmount theAmount = hm.get(taxPercent.stripTrailingZeros());
if (theAmount == null) {
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)));
BigDecimal factor = taxPercent.divide(new BigDecimal(100));
@@ -205,7 +207,8 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
VATAmount theAmount = hm.get(taxPercent.stripTrailingZeros());
if (theAmount == null) {
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)));
BigDecimal factor = taxPercent.divide(new BigDecimal(100));

View File

@@ -37,12 +37,23 @@ public class VATAmount {
this.basis = basis;
this.calculated = calculated;
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;
String categoryCode;
String dueDateTypeCode;
public BigDecimal getApplicablePercent() {
return applicablePercent;
}
@@ -95,12 +106,20 @@ public class VATAmount {
this.categoryCode = categoryCode;
}
public String getDueDateTypeCode() {
return dueDateTypeCode;
}
public void setDueDateTypeCode(String dueDateTypeCode) {
this.dueDateTypeCode = dueDateTypeCode;
}
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) {
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);
}
}

View File

@@ -271,6 +271,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
if (amount != null) {
final String amountCategoryCode = amount.getCategoryCode();
final String amountDueDateTypeCode = amount.getDueDateTypeCode();
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
xml += "<ram:ApplicableTradeTax>"
+ "<ram:CalculatedAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getCalculated())
@@ -279,6 +280,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
+ (displayExemptionReason ? exemptionReason : "")
+ "<ram:BasisAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
+ "<ram:CategoryCode>" + amount.getCategoryCode() + "</ram:CategoryCode>"
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")
+ "<ram:ApplicablePercent>" + vatFormat(currentTaxPercent)
+ "</ram:ApplicablePercent></ram:ApplicableTradeTax>";
}

View File

@@ -618,6 +618,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
if (amount != null) {
final String amountCategoryCode = amount.getCategoryCode();
final String amountDueDateTypeCode = amount.getDueDateTypeCode();
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
if (getProfile() != Profiles.getByName("Minimum")) {
@@ -628,6 +629,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ (displayExemptionReason ? exemptionReason : "")
+ "<ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
+ "<ram:CategoryCode>" + amountCategoryCode + "</ram:CategoryCode>"
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")
+ "<ram:RateApplicablePercent>"
+ vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent></ram:ApplicableTradeTax>";
}

View File

@@ -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";
}

View File

@@ -33,6 +33,8 @@ import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import junit.framework.TestCase;
import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants;
import org.xmlunit.builder.Input;
import org.xmlunit.xpath.JAXPXPathEngine;
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")))
.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)))
.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) {
e.printStackTrace();
@@ -429,6 +431,8 @@ public class ZF2PushTest extends TestCase {
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
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString()
.isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE);
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE);
try {