Merge branch 'master' of github.com:ZUGFeRD/mustangproject

This commit is contained in:
Jochen Stärk
2026-05-07 13:15:19 +02:00
17 changed files with 719 additions and 396 deletions

View File

@@ -21,7 +21,9 @@ public class CashDiscount implements IZUGFeRDCashDiscount {
/***
* the period (usually days) count how long the percent apply
*/
protected Integer days=null;
protected Integer days;
protected BigDecimal basisAmount;
/***
* Create a cash discount (skonto) with the specified height in the specified period.
@@ -61,18 +63,31 @@ public class CashDiscount implements IZUGFeRDCashDiscount {
return this;
}
public BigDecimal getBasisAmount() {
return basisAmount;
}
public CashDiscount setBasisAmount(BigDecimal basisAmount) {
this.basisAmount = basisAmount;
return this;
}
/***
* @return this particular cash discount as cross industry invoice XML
*/
@JsonIgnore
public String getAsCII() {
return "<ram:SpecifiedTradePaymentTerms>"+
"<ram:Description>Cash Discount</ram:Description>"+
" <ram:ApplicableTradePaymentDiscountTerms>"+
" <ram:BasisPeriodMeasure unitCode=\"DAY\">"+days+"</ram:BasisPeriodMeasure>"+
" <ram:CalculationPercent>"+XMLTools.nDigitFormat(percent,3)+"</ram:CalculationPercent>"+
" </ram:ApplicableTradePaymentDiscountTerms>"+
"</ram:SpecifiedTradePaymentTerms>";
String s = "<ram:SpecifiedTradePaymentTerms>"
+ " <ram:Description>Cash Discount</ram:Description>"
+ " <ram:ApplicableTradePaymentDiscountTerms>"
+ " <ram:BasisPeriodMeasure unitCode=\"DAY\">" + days + "</ram:BasisPeriodMeasure>";
if (basisAmount != null) {
s += " <ram:BasisAmount>" + XMLTools.nDigitFormat(basisAmount, 2) + "</ram:BasisAmount>";
}
s += " <ram:CalculationPercent>"+XMLTools.nDigitFormat(percent,3)+"</ram:CalculationPercent>"+
" </ram:ApplicableTradePaymentDiscountTerms>"+
"</ram:SpecifiedTradePaymentTerms>";
return s;
}
/***
@@ -84,8 +99,4 @@ public class CashDiscount implements IZUGFeRDCashDiscount {
public String getAsXRechnung() {
return "#SKONTO#TAGE="+days+"#PROZENT="+XMLTools.nDigitFormat(percent,2)+"#\n";
}
}

View File

@@ -41,28 +41,6 @@ public class DXExporterFromA1 extends DXExporterFromA3 {
private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser (dataSource));
}
/***
* internal helper function: get namespace for order-x
* @param ver the delivery-x version
* @return the URN of the namespace
*/
@Override
public String getNamespaceForVersion(int ver) {
// As of late 2022 the Delivery-X standard is not yet published. See specification:
// Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022
// Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3
// http://docplayer.org/230301085-Der-digitale-lieferschein-dls.html
return "urn:factur-x:pdfa:CrossIndustryDocument:despatchadvice:1p0#";
}
/***
* internal helper: returns the namespace prefix for the given order-x version number
* @param ver the ox version
* @return the namespace prefix as string, without colon
*/
@Override
public String getPrefixForVersion(int ver) {
return "fx";
}
private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException {
/*

View File

@@ -130,6 +130,29 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
private boolean attachZUGFeRDHeaders = true;
/***
* internal helper function: get namespace for order-x
* @param ver the delivery-x version
* @return the URN of the namespace
*/
@Override
public String getNamespaceForVersion(int ver) {
// As of late 2022 the Delivery-X standard is not yet published. See specification:
// Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022
// Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3
// http://docplayer.org/230301085-Der-digitale-lieferschein-dls.html
return "urn:factur-x:pdfa:CrossIndustryDocument:despatchadvice:1p0#";
}
/***
* internal helper: returns the namespace prefix for the given order-x version number
* @param ver the ox version
* @return the namespace prefix as string, without colon
*/
@Override
public String getPrefixForVersion(int ver) {
return "fx";
}
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)

View File

@@ -41,24 +41,6 @@ public class OXExporterFromA1 extends OXExporterFromA3 {
private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser(dataSource));
}
/***
* internal helper function: get namespace for order-x
* @param ver the order-x version
* @return the URN of the namespace
*/
@Override
public String getNamespaceForVersion(int ver) {
return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#";
}
/***
* internal helper: returns the namespace prefix for the given order-x version number
* @param ver the ox version
* @return the namespace prefix as string, without colon
*/
@Override
public String getPrefixForVersion(int ver) {
return "fx";
}
private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException {
/*

View File

@@ -126,9 +126,27 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
*/
protected String orderXDocumentType = "ORDER";
private boolean attachZUGFeRDHeaders = true;
/***
* internal helper function: get namespace for order-x
* @param ver the order-x version
* @return the URN of the namespace
*/
@Override
public String getNamespaceForVersion(int ver) {
return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#";
}
/***
* internal helper: returns the namespace prefix for the given order-x version number
* @param ver the ox version
* @return the namespace prefix as string, without colon
*/
@Override
public String getPrefixForVersion(int ver) {
return "fx";
}
/** Defines whether attachments to the PDF should be using FLATE compression */
private boolean compressionEnabled = false;

View File

@@ -711,6 +711,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ "</ram:ActualDeliverySupplyChainEvent>";
}
if (trans.getDespatchAdviceReferencedDocumentID() != null && !trans.getDespatchAdviceReferencedDocumentID().trim().isEmpty()) {
xml += "<ram:DespatchAdviceReferencedDocument>";
xml += "<ram:IssuerAssignedID>" + XMLTools.encodeXML(trans.getDespatchAdviceReferencedDocumentID()) + "</ram:IssuerAssignedID>";
xml += "</ram:DespatchAdviceReferencedDocument>";
}
if (trans.getDeliveryNoteReferencedDocumentID() != null && !trans.getDeliveryNoteReferencedDocumentID().trim().isEmpty()) {
xml += "<ram:DeliveryNoteReferencedDocument>";
xml += "<ram:IssuerAssignedID>" + XMLTools.encodeXML(trans.getDeliveryNoteReferencedDocumentID()) + "</ram:IssuerAssignedID>";
@@ -719,14 +725,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:FormattedIssueDateTime><qdt:DateTimeString format=\"102\">"+XMLTools.encodeXML(dateFormat102.format(trans.getDeliveryNoteReferencedDocumentDate()))+"</qdt:DateTimeString></ram:FormattedIssueDateTime>";
}
xml += "</ram:DeliveryNoteReferencedDocument>";
}
if (trans.getDespatchAdviceReferencedDocumentID() != null && !trans.getDespatchAdviceReferencedDocumentID().trim().isEmpty()) {
xml += "<ram:DespatchAdviceReferencedDocument>";
xml += "<ram:IssuerAssignedID>" + XMLTools.encodeXML(trans.getDespatchAdviceReferencedDocumentID()) + "</ram:IssuerAssignedID>";
xml += "</ram:DespatchAdviceReferencedDocument>";
}
xml += "</ram:ApplicableHeaderTradeDelivery>";
@@ -822,8 +820,14 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:SpecifiedTradeAllowanceCharge>" +
"<ram:ChargeIndicator>" +
"<udt:Indicator>true</udt:Indicator>" +
"</ram:ChargeIndicator>" +
"<ram:ActualAmount>" + currencyFormat(charge.getTotalAmount(calc)) + "</ram:ActualAmount>";
"</ram:ChargeIndicator>";
if (charge.getPercent() != null && (profile == Profiles.getByName("EN16931") || (profile == Profiles.getByName("EXTENDED")))) {
xml += "<ram:CalculationPercent>" + vatFormat(charge.getPercent()) + "</ram:CalculationPercent>";
}
if (charge.getBasisAmount() != null && (profile == Profiles.getByName("EN16931") || (profile == Profiles.getByName("EXTENDED")))) {
xml += "<ram:BasisAmount>" + currencyFormat(charge.getBasisAmount()) + "</ram:BasisAmount>";
}
xml += "<ram:ActualAmount>" + currencyFormat(charge.getTotalAmount(calc)) + "</ram:ActualAmount>";
if (charge.getReasonCode() != null) {
xml += "<ram:ReasonCode>" + charge.getReasonCode() + "</ram:ReasonCode>";
}
@@ -866,14 +870,20 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:SpecifiedTradeAllowanceCharge>" +
"<ram:ChargeIndicator>" +
"<udt:Indicator>false</udt:Indicator>" +
"</ram:ChargeIndicator>" +
"<ram:ActualAmount>" + currencyFormat(allowance.getTotalAmount(calc)) + "</ram:ActualAmount>";
if (allowance.getReason() != null) {
xml += "<ram:Reason>" + XMLTools.encodeXML(allowance.getReason()) + "</ram:Reason>";
"</ram:ChargeIndicator>";
if (allowance.getPercent() != null) {
xml += "<ram:CalculationPercent>" + vatFormat(allowance.getPercent()) + "</ram:CalculationPercent>";
}
if (allowance.getBasisAmount() != null) {
xml += "<ram:BasisAmount>" + currencyFormat(allowance.getBasisAmount()) + "</ram:BasisAmount>";
}
xml += "<ram:ActualAmount>" + currencyFormat(allowance.getTotalAmount(calc)) + "</ram:ActualAmount>";
if (allowance.getReasonCode() != null) {
xml += "<ram:ReasonCode>" + allowance.getReasonCode() + "</ram:ReasonCode>";
}
if (allowance.getReason() != null) {
xml += "<ram:Reason>" + XMLTools.encodeXML(allowance.getReason()) + "</ram:Reason>";
}
xml += "<ram:CategoryTradeTax>" +
"<ram:TypeCode>VAT</ram:TypeCode>" +
"<ram:CategoryCode>" + allowance.getCategoryCode() + "</ram:CategoryCode>";

View File

@@ -78,6 +78,8 @@ import jakarta.activation.FileDataSource;
public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter {
private static final String XML_DESCRIPTION = "Invoice metadata conforming to ZUGFeRD standard (https://www.ferd-net.de/en/standards/zugferd/factur-x)";
private boolean isFacturX = true;
public static final int DefaultZUGFeRDVersion = 2;
@@ -684,8 +686,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
}
PDFAttachGenericFile(doc, filename, relationship,
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
"text/xml", xmlProvider.getXML());
XML_DESCRIPTION, "text/xml", xmlProvider.getXML());
for (FileAttachment attachment : fileAttachments) {
PDFAttachGenericFile(doc, attachment.getFilename(), attachment.getRelation(), attachment.getDescription(), attachment.getMimetype(), attachment.getData());

View File

@@ -764,8 +764,9 @@ public class ZF2PushTest extends TestCase {
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0))).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK"))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReason("Mengenrabatt"))
.addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addCharge(new Charge().setPercent(new BigDecimal(50)).setBasisAmount(price).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK").setReason("Verschiedenes"))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("95").setReason("Mengenrabatt"))
);
String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));