This commit is contained in:
jstaerk
2024-09-10 19:12:48 +02:00
parent 958d268cac
commit 3d9bfbfa36
6 changed files with 96 additions and 28 deletions

View File

@@ -13,6 +13,8 @@ import java.util.HashMap;
public class Product implements IZUGFeRDExportableProduct { public class Product implements IZUGFeRDExportableProduct {
protected String unit, name, sellerAssignedID, buyerAssignedID; protected String unit, name, sellerAssignedID, buyerAssignedID;
protected String description=""; protected String description="";
protected String taxExemptionReason=null;
protected String taxCategoryCode=null;
protected BigDecimal VATPercent; protected BigDecimal VATPercent;
protected boolean isReverseCharge = false; protected boolean isReverseCharge = false;
protected boolean isIntraCommunitySupply = false; protected boolean isIntraCommunitySupply = false;
@@ -66,6 +68,44 @@ public class Product implements IZUGFeRDExportableProduct {
return this; return this;
} }
/***
*
* @return e.g. intra-commnunity supply or small business
*/
@Override
public String getTaxExemptionReason() {
return taxExemptionReason;
}
/***
*
* @param taxExemptionReasonText String e.g. Kleinunternehmer gemäß §19 UStG https://github.com/ZUGFeRD/mustangproject/issues/463
* @return
*/
public Product setTaxExemptionReason(String taxExemptionReasonText) {
taxExemptionReason = taxExemptionReasonText;
return this;
}
/***
*
* @return e.g. S (normal tax), Z=zero rated, E (e.g. small business) or K (intrra community supply)
*/
@Override
public String getTaxCategoryCode() {
return taxCategoryCode;
}
/***
*
* @param code e.g. S (normal tax), Z=zero rated, E (e.g. small business) or K (intrra community supply) see also https://github.com/ZUGFeRD/mustangproject/issues/463
* @return
*/
public Product setTaxCategoryCode(String code) {
taxCategoryCode = code;
return this;
}
@Override @Override
public String getSellerAssignedID() { public String getSellerAssignedID() {

View File

@@ -172,6 +172,10 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
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(), vatDueDateTypeCode); currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode);
String reasonText=currentItem.getProduct().getTaxExemptionReason();
if (reasonText!=null) {
itemVATAmount.setVatExemptionReasonText(reasonText);
}
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);

View File

@@ -32,6 +32,16 @@ import java.math.RoundingMode;
*/ */
public class VATAmount { public class VATAmount {
protected BigDecimal basis, calculated, applicablePercent;
protected String categoryCode;
protected String vatExemptionReasonText;
protected String dueDateTypeCode;
public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) { public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) {
super(); super();
this.basis = basis; this.basis = basis;
@@ -48,35 +58,41 @@ public class VATAmount {
this.dueDateTypeCode = dueDateTypeCode; this.dueDateTypeCode = dueDateTypeCode;
} }
BigDecimal basis, calculated, applicablePercent;
String categoryCode;
String dueDateTypeCode;
public BigDecimal getApplicablePercent() { public BigDecimal getApplicablePercent() {
return applicablePercent; return applicablePercent;
} }
public void setApplicablePercent(BigDecimal applicablePercent) { public VATAmount setApplicablePercent(BigDecimal applicablePercent) {
this.applicablePercent = applicablePercent; this.applicablePercent = applicablePercent;
return this;
} }
public BigDecimal getBasis() { public BigDecimal getBasis() {
return basis; return basis;
} }
public void setBasis(BigDecimal basis) { public VATAmount setBasis(BigDecimal basis) {
this.basis = basis.setScale(2, RoundingMode.HALF_UP); this.basis = basis.setScale(2, RoundingMode.HALF_UP);
return this;
} }
public BigDecimal getCalculated() { public BigDecimal getCalculated() {
return calculated; return calculated;
} }
public void setCalculated(BigDecimal calculated) { public VATAmount setCalculated(BigDecimal calculated) {
this.calculated = calculated; this.calculated = calculated;
return this;
}
public String getVatExemptionReasonText() {
return vatExemptionReasonText;
}
public VATAmount setVatExemptionReasonText(String theText) {
this.vatExemptionReasonText = theText;
return this;
} }
/** /**
@@ -94,24 +110,27 @@ public class VATAmount {
* @deprecated Use {@link #setCategoryCode(String)} instead * @deprecated Use {@link #setCategoryCode(String)} instead
*/ */
@Deprecated @Deprecated
public void setDocumentCode(String documentCode) { public VATAmount setDocumentCode(String documentCode) {
this.categoryCode = documentCode; this.categoryCode = documentCode;
return this;
} }
public String getCategoryCode() { public String getCategoryCode() {
return categoryCode; return categoryCode;
} }
public void setCategoryCode(String categoryCode) { public VATAmount setCategoryCode(String categoryCode) {
this.categoryCode = categoryCode; this.categoryCode = categoryCode;
return this;
} }
public String getDueDateTypeCode() { public String getDueDateTypeCode() {
return dueDateTypeCode; return dueDateTypeCode;
} }
public void setDueDateTypeCode(String dueDateTypeCode) { public VATAmount setDueDateTypeCode(String dueDateTypeCode) {
this.dueDateTypeCode = dueDateTypeCode; this.dueDateTypeCode = dueDateTypeCode;
return this;
} }
public VATAmount add(VATAmount v) { public VATAmount add(VATAmount v) {

View File

@@ -654,12 +654,17 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
final String amountDueDateTypeCode = amount.getDueDateTypeCode(); 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")) {
String exemptionReasonTextXML = "";
if ((displayExemptionReason) && (amount.getVatExemptionReasonText() != null)) {
exemptionReasonTextXML = "<ram:ExemptionReason>" + XMLTools.encodeXML(amount.getVatExemptionReasonText()) + "</ram:ExemptionReason>";
}
xml += "<ram:ApplicableTradeTax>" xml += "<ram:ApplicableTradeTax>"
+ "<ram:CalculatedAmount>" + currencyFormat(amount.getCalculated()) + "<ram:CalculatedAmount>" + currencyFormat(amount.getCalculated())
+ "</ram:CalculatedAmount>" //currencyID=\"EUR\" + "</ram:CalculatedAmount>" //currencyID=\"EUR\"
+ "<ram:TypeCode>VAT</ram:TypeCode>" + "<ram:TypeCode>VAT</ram:TypeCode>"
+ (displayExemptionReason ? exemptionReason : "") + exemptionReasonTextXML
+ "<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>" : "") + (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")

View File

@@ -30,5 +30,5 @@ public class TaxCategoryCodeTypeConstants {
public static final String UNTAXEDSERVICE = "O"; public static final String UNTAXEDSERVICE = "O";
public static final String INTRACOMMUNITY = "K"; public static final String INTRACOMMUNITY = "K";
public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE).collect(Collectors.toSet()); public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT).collect(Collectors.toSet());
} }

View File

@@ -125,7 +125,7 @@ public class ZF2PushTest extends TestCase {
String number = "123"; String number = "123";
String priceStr = "1.00"; String priceStr = "1.00";
String senderDescription = "Kein Kleinunternehmer"; String senderDescription = "Kleinunternehmer";
String taxID = "9990815"; String taxID = "9990815";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
try { try {
@@ -144,7 +144,7 @@ public class ZF2PushTest extends TestCase {
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711") .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"))) .setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE")))
.setNumber(number) .setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(0)).setTaxExemptionReason("Kleinunternehmer gemäß §19 UStG").setTaxCategoryCode("E"), price, new BigDecimal(1.0)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
@@ -152,7 +152,7 @@ public class ZF2PushTest extends TestCase {
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised"); fail("IOException should not be raised");
} }
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PDF); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_ATTACHMENTSPDF);
Invoice i= null; Invoice i= null;
try { try {
i = zii.extractInvoice(); i = zii.extractInvoice();
@@ -170,7 +170,7 @@ public class ZF2PushTest extends TestCase {
assertTrue(zi.getUTF8().contains(taxID)); assertTrue(zi.getUTF8().contains(taxID));
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals("1.19", zi.getAmount()); assertEquals("1.00", zi.getAmount());
assertEquals(orgname, zi.getHolder()); assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference()); assertEquals(number, zi.getForeignReference());
try { try {