Merge branch 'ZUGFeRD:master' into fix-logback

This commit is contained in:
Adrian-Devries
2025-01-06 11:41:17 +01:00
committed by GitHub
47 changed files with 5499 additions and 4540 deletions

View File

@@ -1,8 +1,25 @@
- 639
- 633
-626,
-622,
-356
allow to add includedNotes with type
- - 645
- 631 multiple invoice referenced documents
- 629
- 630 #296 #565
-
2.15.2
=======
2024-12-19
- correcly write charge reason codes also for non-Xrechnung #617 - correcly write charge reason codes also for non-Xrechnung #617
- correctly import additional referenced documents into invoice/corrected setting of attachments from jackson - correctly import additional referenced documents into invoice/corrected setting of attachments from jackson
- corrected parseException structure - corrected parseException structure
- allow 1p0 as potential xmp version number - allow 1p0 as potential xmp version number
- #618 import BT-20 - #618 import BT-20
- #599 add tax category code for free export
- #600 Fixes a problem where a stream was not safely closed
2.15.1 2.15.1
======= =======

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
@@ -12,7 +12,7 @@
should also work for XRechnung/CII. should also work for XRechnung/CII.
</name> </name>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.compilerVersion>11</maven.compiler.compilerVersion> <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
@@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId> <artifactId>validator</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with <!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
--> -->
@@ -45,7 +45,7 @@
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId> <artifactId>logback-core</artifactId>
<version>1.2.13</version> <version>1.5.13</version>
</dependency> </dependency>
<dependency> <dependency>
<!-- This library is needed so that logback stderr output is sent to, well, stderr, otherwise it lands in stdout --> <!-- This library is needed so that logback stderr output is sent to, well, stderr, otherwise it lands in stdout -->

View File

@@ -19,6 +19,7 @@
package org.mustangproject.commandline; package org.mustangproject.commandline;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.apache.commons.io.FilenameUtils;
import org.mustangproject.CII.CIIToUBL; import org.mustangproject.CII.CIIToUBL;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
@@ -84,6 +85,7 @@ public class Main {
+ " [--logAppend <text>]: text to be added to log line\n" + " [--logAppend <text>]: text to be added to log line\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--source <filename>]: input PDF or XML file\n" + " [--source <filename>]: input PDF or XML file\n"
+ " [--log-as-pdf]: save log output as pdf\n"
+ " --action validateExpectInvalid validate directory expecting negative results \n" + " --action validateExpectInvalid validate directory expecting negative results \n"
+ " [--no-notices]: refrain from reporting notices\n" + " [--no-notices]: refrain from reporting notices\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
@@ -357,6 +359,7 @@ public class Main {
options.addOption(new Option("d", "directory", true, "which directory to operate on")); options.addOption(new Option("d", "directory", true, "which directory to operate on"));
options.addOption(new Option("i", "ignorefileextension", false, "ignore non-matching file extensions")); options.addOption(new Option("i", "ignorefileextension", false, "ignore non-matching file extensions"));
options.addOption(new Option("l", "listfromstdin", false, "take list of files from commandline")); options.addOption(new Option("l", "listfromstdin", false, "take list of files from commandline"));
options.addOption(new Option("log-as-pdf", "log-as-pdf", false, "saving log output to pdf file"));
boolean optionsRecognized = false; boolean optionsRecognized = false;
String action = ""; String action = "";
@@ -380,6 +383,7 @@ public class Main {
String format = cmd.getOptionValue("format"); String format = cmd.getOptionValue("format");
String lang = cmd.getOptionValue("language"); String lang = cmd.getOptionValue("language");
Boolean noNotices = cmd.hasOption("no-notices"); Boolean noNotices = cmd.hasOption("no-notices");
Boolean LogAsPDF = cmd.hasOption("log-as-pdf");
String zugferdVersion = cmd.getOptionValue("version"); String zugferdVersion = cmd.getOptionValue("version");
String zugferdProfile = cmd.getOptionValue("profile"); String zugferdProfile = cmd.getOptionValue("profile");
@@ -427,7 +431,7 @@ public class Main {
performUBL(sourceName, outName); performUBL(sourceName, outName);
optionsRecognized = true; optionsRecognized = true;
} else if ((action != null) && (action.equals("validate"))) { } else if ((action != null) && (action.equals("validate"))) {
optionsRecognized = performValidate(sourceName, noNotices, cmd.getOptionValue("logAppend")); optionsRecognized = performValidate(sourceName, noNotices, cmd.getOptionValue("logAppend"), LogAsPDF);
} else if ((action != null) && (action.equals("validateExpectValid"))) { } else if ((action != null) && (action.equals("validateExpectValid"))) {
optionsRecognized = performValidateExpect(true, directoryName); optionsRecognized = performValidateExpect(true, directoryName);
} else if ((action != null) && (action.equals("validateExpectInvalid"))) { } else if ((action != null) && (action.equals("validateExpectInvalid"))) {
@@ -454,7 +458,7 @@ public class Main {
} }
private static boolean performValidate(String sourceName, boolean noNotices, String logAppend) { private static boolean performValidate(String sourceName, boolean noNotices, String logAppend, boolean createLogAsPDF) {
boolean optionsRecognized; boolean optionsRecognized;
if (sourceName == null) { if (sourceName == null) {
sourceName = getFilenameFromUser("Source PDF or XML", "invoice.pdf", "pdf|xml", true, false); sourceName = getFilenameFromUser("Source PDF or XML", "invoice.pdf", "pdf|xml", true, false);
@@ -466,7 +470,16 @@ public class Main {
if (noNotices) { if (noNotices) {
zfv.disableNotices(); zfv.disableNotices();
} }
System.out.println(zfv.validate(sourceName));
String validationResultXML = zfv.validate(sourceName);
System.out.println(validationResultXML);
if( createLogAsPDF) {
ValidationLogVisualizer vlvi = new ValidationLogVisualizer();
String fileBasename = FilenameUtils.getBaseName(sourceName);
vlvi.toPDF(validationResultXML, fileBasename + "_result.pdf");
}
optionsRecognized = !zfv.hasOptionsError(); optionsRecognized = !zfv.hasOptionsError();
if (!zfv.wasCompletelyValid()) { if (!zfv.wasCompletelyValid()) {
System.exit(-1); System.exit(-1);

View File

@@ -3,13 +3,13 @@
<parent> <parent>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>library</artifactId> <artifactId>library</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII)</name> <name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII)</name>
<description>FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT <description>FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT
@@ -200,21 +200,51 @@
</manifest> </manifest>
<manifestSections> <manifestSections>
<manifestSection> <manifestSection>
<name>FreeSans.ttf</name> <name>SourceSansPro-Regular.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>font/ttf</Content-Type> <Content-Type>font/ttf</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
<manifestSection> <manifestSection>
<name>FreeSerif.ttf</name> <name>SourceSansPro-It.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>application/x-font</Content-Type> <Content-Type>font/ttf</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
<manifestSection> <manifestSection>
<name>Times-Bold.ttf</name> <name>SourceSansPro-Bold.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>application/x-font</Content-Type> <Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSansPro-BoldIt.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-Regular.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-It.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-Bold.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-BoldIt.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
</manifestSections> </manifestSections>

View File

@@ -3,11 +3,14 @@ package org.mustangproject.Exceptions;
import java.text.ParseException; import java.text.ParseException;
/*** /***
* will be thrown if a invoice cant be reproduced numerically * will be thrown if an invoice cant be reproduced numerically
*/ */
public class ArithmetricException extends ParseException { public class ArithmetricException extends ParseException {
public ArithmetricException() { public ArithmetricException() {
super( this("");
"Could not reproduce the invoice, this could mean that it could not be read properly", 0); }
public ArithmetricException(String details) {
super("Could not reproduce the invoice. " + details, 0);
} }
} }

View File

@@ -38,7 +38,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Invoice implements IExportableTransaction { public class Invoice implements IExportableTransaction {
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null; protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
protected Date issueDate = null, dueDate = null, deliveryDate = null; protected Date issueDate = null, dueDate = null, deliveryDate = null;
protected TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null; protected TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null;
protected ArrayList<CashDiscount> cashDiscounts = null; protected ArrayList<CashDiscount> cashDiscounts = null;
@@ -57,7 +57,12 @@ public class Invoice implements IExportableTransaction {
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(), protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(),
Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>(); Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>();
protected IZUGFeRDPaymentTerms paymentTerms = null; protected IZUGFeRDPaymentTerms paymentTerms = null;
protected String invoiceReferencedDocumentID = null;
protected Date invoiceReferencedIssueDate; protected Date invoiceReferencedIssueDate;
// New field for storing Invoiced Object Identifier (BG-3)
protected ArrayList<ReferencedDocument> invoiceReferencedDocuments = null;
protected String specifiedProcuringProjectID = null; protected String specifiedProcuringProjectID = null;
protected String specifiedProcuringProjectName = null; protected String specifiedProcuringProjectName = null;
protected String despatchAdviceReferencedDocumentID = null; protected String despatchAdviceReferencedDocumentID = null;
@@ -148,6 +153,7 @@ public class Invoice implements IExportableTransaction {
*/ */
public Invoice setCorrection(String number) { public Invoice setCorrection(String number) {
setInvoiceReferencedDocumentID(number); setInvoiceReferencedDocumentID(number);
addInvoiceReferencedDocument(new ReferencedDocument(number));
documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE; documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE;
return this; return this;
} }
@@ -546,6 +552,26 @@ public class Invoice implements IExportableTransaction {
return this; return this;
} }
// Getter for BG-3
@Override
public ArrayList<ReferencedDocument> getInvoiceReferencedDocuments() {
return invoiceReferencedDocuments;
}
// Setter for BG-3
public void setInvoiceReferencedDocuments(ArrayList<ReferencedDocument> invoiceReferencedDocuments) {
this.invoiceReferencedDocuments = invoiceReferencedDocuments;
}
// Method to add a single ReferencedDocument for BG-3
public Invoice addInvoiceReferencedDocument(ReferencedDocument doc) {
if (invoiceReferencedDocuments == null) {
invoiceReferencedDocuments = new ArrayList<>();
}
invoiceReferencedDocuments.add(doc);
return this;
}
@Override @Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() { public IZUGFeRDAllowanceCharge[] getZFAllowances() {
if (Allowances.isEmpty()) { if (Allowances.isEmpty()) {

View File

@@ -352,7 +352,7 @@ public class Item implements IZUGFeRDExportableItem {
/*** /***
* Adds a item level addition to the price (will be multiplied by quantity) * Adds a item level addition to the price (will be multiplied by quantity)
* @see org.mustangproject.Charge * @see Charge
* @param izac a relative or absolute charge * @param izac a relative or absolute charge
* @return fluent setter * @return fluent setter
*/ */
@@ -363,7 +363,7 @@ public class Item implements IZUGFeRDExportableItem {
/*** /***
* Adds a item level reduction the price (will be multiplied by quantity) * Adds a item level reduction the price (will be multiplied by quantity)
* @see org.mustangproject.Allowance * @see Allowance
* @param izac a relative or absolute allowance * @param izac a relative or absolute allowance
* @return fluent setter * @return fluent setter
*/ */
@@ -383,10 +383,23 @@ public class Item implements IZUGFeRDExportableItem {
} }
notes.add(text); notes.add(text);
addNote(IncludedNote.unspecifiedNote(text));
return this;
}
/***
* adds categorized item level freetext fields (includednote)
* @param theNote IncludedNote to add
* @return fluent setter
*/
public Item addNote(IncludedNote theNote) {
if (includedNotes == null) { if (includedNotes == null) {
includedNotes = new ArrayList<>(); includedNotes = new ArrayList<>();
} }
includedNotes.add(IncludedNote.unspecifiedNote(text)); includedNotes.add(theNote);
return this; return this;

View File

@@ -2,6 +2,8 @@ package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Date;
import org.mustangproject.ZUGFeRD.IReferencedDocument; import org.mustangproject.ZUGFeRD.IReferencedDocument;
import org.mustangproject.util.NodeMap; import org.mustangproject.util.NodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -13,17 +15,30 @@ public class ReferencedDocument implements IReferencedDocument {
String issuerAssignedID; String issuerAssignedID;
String typeCode; String typeCode;
String referenceTypeCode; String referenceTypeCode;
Date formattedIssueDateTime;
public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode) { public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode) {
this.issuerAssignedID = issuerAssignedID; this(issuerAssignedID);
this.typeCode = typeCode; this.typeCode = typeCode;
this.referenceTypeCode = referenceTypeCode; this.referenceTypeCode = referenceTypeCode;
} }
public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode, Date formattedIssueDateTime) {
this(issuerAssignedID, typeCode, referenceTypeCode);
this.formattedIssueDateTime = formattedIssueDateTime;
}
public ReferencedDocument(String issuerAssignedID, String referenceTypeCode) { public ReferencedDocument(String issuerAssignedID, String referenceTypeCode) {
this(issuerAssignedID, "916", referenceTypeCode); // additional invoice related document
}
public ReferencedDocument(String issuerAssingedID, Date formattedIssueDateTime) {
this(issuerAssingedID);
this.formattedIssueDateTime = formattedIssueDateTime;
}
public ReferencedDocument(String issuerAssignedID) {
this.issuerAssignedID = issuerAssignedID; this.issuerAssignedID = issuerAssignedID;
this.typeCode = "916"; // additional invoice related document
this.referenceTypeCode = referenceTypeCode;
} }
/*** /***
@@ -50,6 +65,15 @@ public class ReferencedDocument implements IReferencedDocument {
this.referenceTypeCode = referenceTypeCode; this.referenceTypeCode = referenceTypeCode;
} }
/**
* issue date of this line
*
* @param formattedIssueDateTime as Date
*/
public void setFormattedIssueDateTime(Date formattedIssueDateTime) {
this.formattedIssueDateTime = formattedIssueDateTime;
}
@Override @Override
public String getIssuerAssignedID() { public String getIssuerAssignedID() {
return issuerAssignedID; return issuerAssignedID;
@@ -65,6 +89,12 @@ public class ReferencedDocument implements IReferencedDocument {
return referenceTypeCode; return referenceTypeCode;
} }
@Override
public Date getFormattedIssueDateTime()
{
return formattedIssueDateTime;
}
public static ReferencedDocument fromNode(Node node) { public static ReferencedDocument fromNode(Node node) {
if (!node.hasChildNodes()) { if (!node.hasChildNodes()) {
return null; return null;
@@ -72,6 +102,7 @@ public class ReferencedDocument implements IReferencedDocument {
NodeMap nodes = new NodeMap(node); NodeMap nodes = new NodeMap(node);
return new ReferencedDocument(nodes.getAsStringOrNull("IssuerAssignedID", "ID"), return new ReferencedDocument(nodes.getAsStringOrNull("IssuerAssignedID", "ID"),
nodes.getAsStringOrNull("TypeCode", "DocumentTypeCode"), nodes.getAsStringOrNull("TypeCode", "DocumentTypeCode"),
nodes.getAsStringOrNull("ReferenceTypeCode")); nodes.getAsStringOrNull("ReferenceTypeCode"),
XMLTools.tryDate(nodes.getAsStringOrNull("FormattedIssueDateTime")));
} }
} }

View File

@@ -30,11 +30,13 @@ package org.mustangproject.ZUGFeRD;
* */ * */
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
import org.mustangproject.IncludedNote; import org.mustangproject.IncludedNote;
import org.mustangproject.ReferencedDocument;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
/*** /***
@@ -424,14 +426,24 @@ public interface IExportableTransaction {
* *
* @return the ID of the document * @return the ID of the document
*/ */
@Deprecated
default String getInvoiceReferencedDocumentID() { default String getInvoiceReferencedDocumentID() {
return null; return null;
} }
@Deprecated
default Date getInvoiceReferencedIssueDate() { default Date getInvoiceReferencedIssueDate() {
return null; return null;
} }
/**
* Getter for BG-3
* @return list of documents
*/
default ArrayList<ReferencedDocument> getInvoiceReferencedDocuments() {
return null;
}
/** /**
* get the issue timestamp of the BuyerOrderReferencedDocument, which sits in * get the issue timestamp of the BuyerOrderReferencedDocument, which sits in
* the ApplicableSupplyChainTradeAgreement * the ApplicableSupplyChainTradeAgreement

View File

@@ -1,5 +1,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.util.Date;
public interface IReferencedDocument { public interface IReferencedDocument {
/*** /***
@@ -20,4 +22,12 @@ public interface IReferencedDocument {
*/ */
String getReferenceTypeCode(); String getReferenceTypeCode();
/***
*
* issue date of this line
*
* @return date of the issue
*/
Date getFormattedIssueDateTime();
} }

View File

@@ -459,6 +459,19 @@ public class OXPullProvider extends ZUGFeRD2PullProvider {
} }
xml += "</ram:InvoiceReferencedDocument>"; xml += "</ram:InvoiceReferencedDocument>";
} }
if (trans.getInvoiceReferencedDocuments() != null) {
for (var doc : trans.getInvoiceReferencedDocuments()) {
xml += "<ram:InvoiceReferencedDocument>"
+ "<ram:IssuerAssignedID>"
+ XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>";
if (doc.getFormattedIssueDateTime() != null) {
xml += "<ram:FormattedIssueDateTime>"
+ DATE.qdtFormat(doc.getFormattedIssueDateTime())
+ "</ram:FormattedIssueDateTime>";
}
xml += "</ram:InvoiceReferencedDocument>";
}
}
xml += "</ram:ApplicableHeaderTradeSettlement>"; xml += "</ram:ApplicableHeaderTradeSettlement>";
// + " <IncludedSupplyChainTradeLineItem>" // + " <IncludedSupplyChainTradeLineItem>"

View File

@@ -4,7 +4,10 @@ import static java.math.BigDecimal.ZERO;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/*** /***
@@ -91,17 +94,14 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
} }
private String getAllowanceChargeReasonForPercent(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) { private String getAllowanceChargeReasonForPercent(BigDecimal percent, IZUGFeRDAllowanceCharge[] charges) {
String res = " "; if (charges == null) {
if ((charges != null) && (charges.length > 0)) { return "";
for (IZUGFeRDAllowanceCharge currentCharge : charges) {
if ((percent == null) || (currentCharge.getTaxPercent().compareTo(percent) == 0)
&& currentCharge.getReason() != null) {
res += currentCharge.getReason() + " ";
} }
} return Arrays.stream(charges)
} .filter(currentCharge -> (percent == null || currentCharge.getTaxPercent().compareTo(percent) == 0))
res = res.substring(0, res.length() - 1); .map(IZUGFeRDAllowanceCharge::getReason)
return res; .filter(Objects::nonNull)
.collect(Collectors.joining(" "));
} }
/*** /***

View File

@@ -0,0 +1,148 @@
package org.mustangproject.ZUGFeRD;
import org.apache.fop.apps.*;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.configuration.Configuration;
import org.apache.fop.configuration.ConfigurationException;
import org.apache.fop.configuration.DefaultConfigurationBuilder;
import org.apache.xmlgraphics.util.MimeConstants;
import org.mustangproject.ClasspathResolverURIAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class ValidationLogVisualizer {
public enum Language {
EN,
FR,
DE
}
static final ClassLoader CLASS_LOADER = ValidationLogVisualizer.class.getClassLoader();
private static final String RESOURCE_PATH = "";
private static final Logger LOGGER = LoggerFactory.getLogger(ValidationLogVisualizer.class);
private TransformerFactory mFactory = null;
private Templates mXsltPDFTemplate = null;
public ValidationLogVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory.setURIResolver(new ValidationLogVisualizer.ClasspathResourceURIResolver());
}
protected void applyXSLTToPDF(final String xmlContent, final OutputStream PDFOutstream)
throws TransformerException {
Transformer transformer = mXsltPDFTemplate.newTransformer();
transformer.transform(new StreamSource(new StringReader(xmlContent)), new StreamResult(PDFOutstream));
}
protected String toFOP(final String xmlContent)
throws TransformerException {
try {
if (mXsltPDFTemplate == null) {
mXsltPDFTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/result-pdf.xsl")));
}
} catch (TransformerConfigurationException ex) {
LOGGER.error("Failed to init XSLT templates", ex);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
applyXSLTToPDF(xmlContent, baos);
} catch (Exception e1) {
LOGGER.error("Failed to create PDF", e1);
}
return baos.toString(StandardCharsets.UTF_8);
}
public void toPDF(String xmlLogfileContent, String pdfFilename) {
// the writing part
String result = null;
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
try {
result = this.toFOP(xmlLogfileContent);
} catch ( TransformerException e) {
LOGGER.error("Failed to apply FOP", e);
}
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = null;
try {
cfg = cfgBuilder.build(CLASS_LOADER.getResourceAsStream("fop-config.xconf"));
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}
FopFactoryBuilder builder = new FopFactoryBuilder(new File(".").toURI(), new ClasspathResolverURIAdapter()).setConfiguration(cfg);
// Step 1: Construct a FopFactory by specifying a reference to the configuration file
// (reuse if you plan to render multiple documents!)
FopFactory fopFactory = builder.build();
fopFactory.getFontManager().setResourceResolver(
ResourceResolverFactory.createInternalResourceResolver(
new File(".").toURI(),
new ClasspathResolverURIAdapter()));
FOUserAgent userAgent = fopFactory.newFOUserAgent();
userAgent.getRendererOptions().put("pdf-a-mode", "PDF/A-3b");
// Step 2: Set up output stream.
// Note: Using BufferedOutputStream for performance reasons (helpful with FileOutputStreams).
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfFilename))) {
// Step 3: Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out);
// Step 4: Setup JAXP using identity transformer
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(); // identity transformer
// Step 5: Setup input and output for XSLT transformation
// Setup input stream
Source src = new StreamSource(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8)));
// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Step 6: Start XSLT transformation and FOP processing
transformer.transform(src, res);
} catch (FOPException | IOException | TransformerException e) {
LOGGER.error("Failed to create PDF", e);
}
}
private static class ClasspathResourceURIResolver implements URIResolver {
ClasspathResourceURIResolver() {
// Do nothing, just prevents synthetic access warning.
}
@Override
public Source resolve(String href, String base) throws TransformerException {
return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/" + href));
}
}
}

View File

@@ -829,6 +829,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:Description>" + paymentTermsDescription + "</ram:Description>"; xml += "<ram:Description>" + paymentTermsDescription + "</ram:Description>";
} }
if (trans.getDueDate() != null) {
xml += "<ram:DueDateDateTime>" // $NON-NLS-2$
+ DATE.udtFormat(trans.getDueDate())
+ "</ram:DueDateDateTime>";// 20130704
}
if (trans.getTradeSettlement() != null) { if (trans.getTradeSettlement() != null) {
for (final IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) { for (final IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
if ((payment != null) && (payment instanceof IZUGFeRDTradeSettlementDebit)) { if ((payment != null) && (payment instanceof IZUGFeRDTradeSettlementDebit)) {
@@ -837,12 +843,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
} }
if (trans.getDueDate() != null) {
xml += "<ram:DueDateDateTime>" // $NON-NLS-2$
+ DATE.udtFormat(trans.getDueDate())
+ "</ram:DueDateDateTime>";// 20130704
}
xml += "</ram:SpecifiedTradePaymentTerms>"; xml += "</ram:SpecifiedTradePaymentTerms>";
} else { } else {
xml += buildPaymentTermsXml(); xml += buildPaymentTermsXml();
@@ -893,6 +893,19 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
xml += "</ram:InvoiceReferencedDocument>"; xml += "</ram:InvoiceReferencedDocument>";
} }
if (trans.getInvoiceReferencedDocuments() != null) {
for (var doc : trans.getInvoiceReferencedDocuments()) {
xml += "<ram:InvoiceReferencedDocument>"
+ "<ram:IssuerAssignedID>"
+ XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>";
if (doc.getFormattedIssueDateTime() != null) {
xml += "<ram:FormattedIssueDateTime>"
+ DATE.qdtFormat(doc.getFormattedIssueDateTime())
+ "</ram:FormattedIssueDateTime>";
}
xml += "</ram:InvoiceReferencedDocument>";
}
}
xml += "</ram:ApplicableHeaderTradeSettlement>"; xml += "</ram:ApplicableHeaderTradeSettlement>";
// + "<IncludedSupplyChainTradeLineItem>\n" // + "<IncludedSupplyChainTradeLineItem>\n"

View File

@@ -83,9 +83,10 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter {
} }
protected byte[] filenameToByteArray(String pdfFilename) throws IOException { protected byte[] filenameToByteArray(String pdfFilename) throws IOException {
FileInputStream fileInputStream = new FileInputStream(pdfFilename); try (FileInputStream fileInputStream = new FileInputStream(pdfFilename)) {
return inputstreamToByteArray(fileInputStream); return inputstreamToByteArray(fileInputStream);
} }
}
protected byte[] inputstreamToByteArray(InputStream fileInputStream) throws IOException { protected byte[] inputstreamToByteArray(InputStream fileInputStream) throws IOException {
byte[] bytes = new byte[fileInputStream.available()]; byte[] bytes = new byte[fileInputStream.available()];

View File

@@ -659,6 +659,7 @@ public class ZUGFeRDImporter extends ZUGFeRDInvoiceImporter {
* *
* @return a List of LineItem instances * @return a List of LineItem instances
*/ */
@Deprecated
public List<Item> getLineItemList() { public List<Item> getLineItemList() {
final List<Node> nodeList = getLineItemNodes(); final List<Node> nodeList = getLineItemNodes();
final List<Item> lineItemList = new ArrayList<>(); final List<Item> lineItemList = new ArrayList<>();

View File

@@ -32,6 +32,9 @@ import java.nio.file.StandardOpenOption;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ZUGFeRDInvoiceImporter { public class ZUGFeRDInvoiceImporter {
@@ -472,9 +475,11 @@ public class ZUGFeRDInvoiceImporter {
xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"DuePayableAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"PayableAmount\"]"); xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"DuePayableAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"PayableAmount\"]");
NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
BigDecimal duePayableAmount = null;
if (lineDueNodes.getLength() > 0) { if (lineDueNodes.getLength() > 0) {
duePayableAmount = new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0)));
if (zpp instanceof CalculatedInvoice) { if (zpp instanceof CalculatedInvoice) {
((CalculatedInvoice) zpp).setDuePayable(new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0)))); ((CalculatedInvoice) zpp).setDuePayable(duePayableAmount);
} }
} }
@@ -816,6 +821,23 @@ public class ZUGFeRDInvoiceImporter {
} }
zpp.setInvoiceReferencedDocumentID(extractString("//*[local-name()=\"InvoiceReferencedDocument\"]/*[local-name()=\"IssuerAssignedID\"]|//*[local-name()=\"BillingReference\"]/*[local-name()=\"InvoiceDocumentReference\"]/*[local-name()=\"ID\"]")); zpp.setInvoiceReferencedDocumentID(extractString("//*[local-name()=\"InvoiceReferencedDocument\"]/*[local-name()=\"IssuerAssignedID\"]|//*[local-name()=\"BillingReference\"]/*[local-name()=\"InvoiceDocumentReference\"]/*[local-name()=\"ID\"]"));
xpr = xpath.compile("//*[local-name()=\"InvoiceReferencedDocument\"]");
NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (nodes.getLength() != 0) {
for (int i = 0; i < nodes.getLength(); i++) {
Node currentItemNode = nodes.item(i);
ReferencedDocument doc = ReferencedDocument.fromNode(currentItemNode);
if (doc != null
&& (!Objects.equals(zpp.getInvoiceReferencedDocumentID(), doc.getIssuerAssignedID())
|| !Objects.equals(zpp.getInvoiceReferencedIssueDate(), doc.getFormattedIssueDateTime())))
{
zpp.addInvoiceReferencedDocument(doc);
}
}
}
zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim()); zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim());
String rounding = extractString("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"RoundingAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"Party\"]/*[local-name()=\"PayableRoundingAmount\"]"); String rounding = extractString("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"RoundingAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"Party\"]/*[local-name()=\"PayableRoundingAmount\"]");
@@ -834,7 +856,7 @@ public class ZUGFeRDInvoiceImporter {
} }
xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]"); xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]");
NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (nodes.getLength() != 0) { if (nodes.getLength() != 0) {
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
@@ -938,8 +960,7 @@ public class ZUGFeRDInvoiceImporter {
TransactionCalculator tc = new TransactionCalculator(zpp); TransactionCalculator tc = new TransactionCalculator(zpp);
String expectedStringTotalGross = tc.getGrandTotal() String calculatedPayableTotal = tc.getDuePayable().toPlainString();
.subtract(Objects.requireNonNullElse(zpp.getTotalPrepaidAmount(), BigDecimal.ZERO)).toPlainString();
EStandard whichType; EStandard whichType;
try { try {
whichType = getStandard(); whichType = getStandard();
@@ -947,10 +968,21 @@ public class ZUGFeRDInvoiceImporter {
throw new StructureException("Could not find out if it's an invoice, order, or delivery advice", 0); throw new StructureException("Could not find out if it's an invoice, order, or delivery advice", 0);
} }
if ((whichType != EStandard.despatchadvice) if (whichType != EStandard.despatchadvice && !ignoreCalculationErrors) {
&& ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) // Check calculation if document type allows it and calculation errors should not be ignored
&& (!ignoreCalculationErrors))) {
throw new ArithmetricException(); String payableTotalFromXml = XMLTools.nDigitFormat(Objects.requireNonNullElse(duePayableAmount, expectedGrandTotal), 2);
if (!calculatedPayableTotal.equals(payableTotalFromXml)) {
String moreDetails = "";
try {
moreDetails = " with tax basis " + tc.getTaxBasis() + " and with positions " + tc.getTotal() + " = "
+ Stream.of(tc.trans.getZFItems())
.map(item -> new LineCalculator(item).getItemTotalNetAmount().toPlainString())
.collect(Collectors.joining(" + "));
} catch (Exception ignored) {
}
throw new ArithmetricException("Payable total in XML is " + payableTotalFromXml + ", but calculated total is " + calculatedPayableTotal + moreDetails);
}
} }
} }
return zpp; return zpp;
@@ -1036,6 +1068,7 @@ public class ZUGFeRDInvoiceImporter {
* for PDF embedded files in FX use getFileAttachmentsPDF() * for PDF embedded files in FX use getFileAttachmentsPDF()
* @deprecated use invoice.getAdditionalReferencedDocuments * @deprecated use invoice.getAdditionalReferencedDocuments
*/ */
@Deprecated
public List<FileAttachment> getFileAttachmentsXML() { public List<FileAttachment> getFileAttachmentsXML() {
return new ArrayList<>(Arrays.asList(importedInvoice.getAdditionalReferencedDocuments())); return new ArrayList<>(Arrays.asList(importedInvoice.getAdditionalReferencedDocuments()));
} }
@@ -1067,5 +1100,4 @@ public class ZUGFeRDInvoiceImporter {
LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
} }
} }
} }

View File

@@ -20,42 +20,9 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.BufferedOutputStream; import com.helger.commons.io.stream.StreamHelper;
import java.io.ByteArrayInputStream; import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream; import org.apache.fop.apps.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FopFactoryBuilder;
import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.configuration.Configuration; import org.apache.fop.configuration.Configuration;
import org.apache.fop.configuration.ConfigurationException; import org.apache.fop.configuration.ConfigurationException;
@@ -65,12 +32,19 @@ import org.mustangproject.ClasspathResolverURIAdapter;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.helger.commons.io.stream.StreamHelper;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
public class ZUGFeRDVisualizer { public class ZUGFeRDVisualizer {
@@ -113,7 +87,7 @@ public class ZUGFeRDVisualizer {
* @param fis inputstream (will be consumed) * @param fis inputstream (will be consumed)
* @return (facturx = cii) * @return (facturx = cii)
*/ */
public EStandard findOutStandardFromRootNode(InputStream fis) { private EStandard findOutStandardFromRootNode(InputStream fis) {
String zf1Signature = "CrossIndustryDocument"; String zf1Signature = "CrossIndustryDocument";
String zf2Signature = "CrossIndustryInvoice"; String zf2Signature = "CrossIndustryInvoice";
@@ -144,14 +118,85 @@ public class ZUGFeRDVisualizer {
return null; return null;
} }
public String visualize(String xmlFilename, Language lang) public String visualize(String xmlFilename, Language lang) throws IOException, TransformerException {
throws FileNotFoundException, TransformerException, IOException, SAXException, ParserConfigurationException { FileInputStream fis = new FileInputStream(xmlFilename);
return visualize(fis, lang);
try {
if (mXsltPDFTemplate == null) {
mXsltPDFTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xr-pdf.xsl")));
} }
public String visualize(InputStream inputXml, Language lang) throws IOException, TransformerException {
initTemplates(lang);
String fileContent = new String(IOUtils.toByteArray(inputXml), StandardCharsets.UTF_8);
EStandard thestandard = findOutStandardFromRootNode(new ByteArrayInputStream(fileContent.getBytes(StandardCharsets.UTF_8)));
ByteArrayOutputStream htmlOutput = new ByteArrayOutputStream();
ByteArrayInputStream xmlContentStream = new ByteArrayInputStream(fileContent.getBytes(StandardCharsets.UTF_8));
if (thestandard == EStandard.zugferd) {
applyZF1XSLT(xmlContentStream, htmlOutput);
return htmlOutput.toString(StandardCharsets.UTF_8);
} else if (thestandard == EStandard.facturx) {
//zf2 or fx
applyZF2XSLT(xmlContentStream, htmlOutput);
} else if (thestandard == EStandard.ubl) {
//zf2 or fx
applyUBL2XSLT(xmlContentStream, htmlOutput);
} else if (thestandard == EStandard.ubl_creditnote) {
//zf2 or fx
applyUBLCreditNote2XSLT(xmlContentStream, htmlOutput);
} else if (thestandard == EStandard.orderx) {
//zf2 or fx
applyCIO2XSLT(xmlContentStream, htmlOutput);
} else {
throw new IllegalArgumentException("File does not look like CII or UBL");
}
Optional<InputStream> in = copyStream(htmlOutput);
ByteArrayOutputStream htmlOutStream = new ByteArrayOutputStream();
if (in.isPresent()) {
applyXSLTToHTML(in.get(), htmlOutStream);
}
return htmlOutStream.toString(StandardCharsets.UTF_8);
}
/**
* TODO: jstaerk: why not copy with that simple call: new ByteArrayInputStream(byteArrayOutputStream.toByteArray()) ?
*/
private Optional<InputStream> copyStream(ByteArrayOutputStream byteArrayOutputStream) {
// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
try {
PipedOutputStream out = new PipedOutputStream(in);
new Thread(() -> {
try {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
byteArrayOutputStream.writeTo(out);
} catch (IOException e) {
LOGGER.error("Failed to write to stream", e);
} finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
StreamHelper.close(out);
}
}).start();
} catch (IOException e1) {
LOGGER.error("Failed to create HTML", e1);
return Optional.empty();
}
return Optional.of(in);
}
private void initTemplates(Language lang) throws TransformerConfigurationException {
if (mXsltXRTemplate == null) {
mXsltXRTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
}
if (mXsltHTMLTemplate == null) { if (mXsltHTMLTemplate == null) {
mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource( mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html." + lang.name().toLowerCase() + ".xsl"))); CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html." + lang.name().toLowerCase() + ".xsl")));
@@ -160,90 +205,10 @@ public class ZUGFeRDVisualizer {
mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource( mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt"))); CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt")));
} }
} catch (TransformerConfigurationException ex) {
LOGGER.error("Failed to init XSLT templates", ex);
}
/**
* *
* http://www.unece.org/fileadmin/DAM/cefact/xml/XML-Naming-And-Design-Rules-V2_1.pdf
* http://www.ferd-net.de/upload/Dokumente/FACTUR-X_ZUGFeRD_2p0_Teil1_Profil_EN16931_1p03.pdf
* http://countwordsfree.com/xmlviewer
*/
FileInputStream fis = new FileInputStream(xmlFilename);
String fileContent = "";
try {
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8);
} catch (IOException e2) {
LOGGER.error("Failed to read file content", e2);
}
ByteArrayOutputStream iaos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean doPostProcessing = false;
fis = new FileInputStream(xmlFilename); // fis wont reset() so re-read from beginning
EStandard thestandard = findOutStandardFromRootNode(fis);
fis = new FileInputStream(xmlFilename); // fis wont reset() so re-read from beginning
if (thestandard == EStandard.zugferd) {
applyZF1XSLT(fis, baos);
} else if (thestandard == EStandard.facturx) {
//zf2 or fx
applyZF2XSLT(fis, iaos);
doPostProcessing = true;
} else if (thestandard == EStandard.ubl) {
//zf2 or fx
applyUBL2XSLT(fis, iaos);
doPostProcessing = true;
} else if (thestandard == EStandard.ubl_creditnote) {
//zf2 or fx
applyUBLCreditNote2XSLT(fis, iaos);
doPostProcessing = true;
} else if (thestandard == EStandard.orderx) {
//zf2 or fx
applyCIO2XSLT(fis, iaos);
doPostProcessing = true;
} else {
throw new IllegalArgumentException("File does not look like CII or UBL");
}
if (doPostProcessing) {
// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
PipedOutputStream out;
try {
out = new PipedOutputStream(in);
new Thread(new Runnable() {
public void run() {
try {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
iaos.writeTo(out);
} catch (IOException e) {
LOGGER.error("Failed to write to stream", e);
} finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
StreamHelper.close(out);
}
}
}).start();
applyXSLTToHTML(in, baos);
} catch (IOException e1) {
LOGGER.error("Failed to create HTML", e1);
}
}
return baos.toString(StandardCharsets.UTF_8);
} }
protected String toFOP(String xmlFilename) protected String toFOP(String xmlFilename)
throws FileNotFoundException, TransformerException { throws IOException, TransformerException {
FileInputStream fis = new FileInputStream(xmlFilename); FileInputStream fis = new FileInputStream(xmlFilename);
EStandard theStandard = findOutStandardFromRootNode(fis); EStandard theStandard = findOutStandardFromRootNode(fis);
@@ -253,7 +218,7 @@ public class ZUGFeRDVisualizer {
} }
protected String toFOP(InputStream is, EStandard theStandard) protected String toFOP(InputStream is, EStandard theStandard)
throws FileNotFoundException, TransformerException { throws TransformerException, IOException {
try { try {
if (mXsltPDFTemplate == null) { if (mXsltPDFTemplate == null) {
@@ -265,7 +230,6 @@ public class ZUGFeRDVisualizer {
} }
ByteArrayOutputStream iaos = new ByteArrayOutputStream(); ByteArrayOutputStream iaos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//zf2 or fx //zf2 or fx
if (theStandard == EStandard.facturx) { if (theStandard == EStandard.facturx) {
@@ -277,33 +241,11 @@ public class ZUGFeRDVisualizer {
} }
PipedInputStream in = new PipedInputStream(); Optional<InputStream> in = copyStream(iaos);
PipedOutputStream out; ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { if (in.isPresent()) {
out = new PipedOutputStream(in); applyXSLTToPDF(in.get(), baos);
new Thread(new Runnable() {
public void run() {
try {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
iaos.writeTo(out);
} catch (IOException e) {
LOGGER.error("Failed to write to stream", e);
} finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
StreamHelper.close(out);
} }
}
}).start();
applyXSLTToPDF(in, baos);
} catch (IOException e1) {
LOGGER.error("Failed to create PDF", e1);
}
return baos.toString(StandardCharsets.UTF_8); return baos.toString(StandardCharsets.UTF_8);
} }
@@ -319,7 +261,7 @@ public class ZUGFeRDVisualizer {
*/ */
try { try {
result = this.toFOP(XMLinputFile.getAbsolutePath()); result = this.toFOP(XMLinputFile.getAbsolutePath());
} catch (FileNotFoundException | TransformerException e) { } catch (TransformerException | IOException e) {
LOGGER.error("Failed to apply FOP", e); LOGGER.error("Failed to apply FOP", e);
} }
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
@@ -373,7 +315,7 @@ public class ZUGFeRDVisualizer {
} }
} }
protected void applyZF2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyZF2XSLT(final InputStream xmlFile, final OutputStream htmlOutStream)
throws TransformerException { throws TransformerException {
if (mXsltXRTemplate == null) { if (mXsltXRTemplate == null) {
mXsltXRTemplate = mFactory.newTemplates( mXsltXRTemplate = mFactory.newTemplates(
@@ -382,10 +324,10 @@ public class ZUGFeRDVisualizer {
} }
Transformer transformer = mXsltXRTemplate.newTransformer(); Transformer transformer = mXsltXRTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutStream));
} }
protected void applyCIO2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyCIO2XSLT(final InputStream xmlFile, final OutputStream htmlOutstream)
throws TransformerException { throws TransformerException {
if (mXsltCIOTemplate == null) { if (mXsltCIOTemplate == null) {
mXsltCIOTemplate = mFactory.newTemplates( mXsltCIOTemplate = mFactory.newTemplates(
@@ -393,10 +335,10 @@ public class ZUGFeRDVisualizer {
} }
Transformer transformer = mXsltCIOTemplate.newTransformer(); Transformer transformer = mXsltCIOTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutstream));
} }
protected void applyUBL2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyUBL2XSLT(final InputStream xmlFile, final OutputStream htmlOutStream)
throws TransformerException { throws TransformerException {
if (mXsltUBLTemplate == null) { if (mXsltUBLTemplate == null) {
mXsltUBLTemplate = mFactory.newTemplates( mXsltUBLTemplate = mFactory.newTemplates(
@@ -404,10 +346,10 @@ public class ZUGFeRDVisualizer {
} }
Transformer transformer = mXsltUBLTemplate.newTransformer(); Transformer transformer = mXsltUBLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutStream));
} }
protected void applyUBLCreditNote2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyUBLCreditNote2XSLT(final InputStream xmlFile, final OutputStream htmlOutStream)
throws TransformerException { throws TransformerException {
if (mXsltUBLTemplate == null) { if (mXsltUBLTemplate == null) {
mXsltUBLTemplate = mFactory.newTemplates( mXsltUBLTemplate = mFactory.newTemplates(
@@ -415,28 +357,30 @@ public class ZUGFeRDVisualizer {
} }
Transformer transformer = mXsltUBLTemplate.newTransformer(); Transformer transformer = mXsltUBLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutStream));
} }
protected void applyZF1XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyZF1XSLT(final InputStream xmlFile, final OutputStream htmlOutStream)
throws TransformerException { throws TransformerException {
Transformer transformer = mXsltZF1HTMLTemplate.newTransformer(); Transformer transformer = mXsltZF1HTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutStream));
} }
protected void applyXSLTToHTML(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyXSLTToHTML(final InputStream xmlFile, final OutputStream htmlOutStream)
throws TransformerException { throws TransformerException, IOException {
Transformer transformer = mXsltHTMLTemplate.newTransformer(); Transformer transformer = mXsltHTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(htmlOutStream));
xmlFile.close();
} }
protected void applyXSLTToPDF(final InputStream xmlFile, final OutputStream PDFOutstream) protected void applyXSLTToPDF(final InputStream xmlFile, final OutputStream PDFOutstream)
throws TransformerException { throws TransformerException, IOException {
Transformer transformer = mXsltPDFTemplate.newTransformer(); Transformer transformer = mXsltPDFTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(PDFOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(PDFOutstream));
xmlFile.close();
} }
private static class ClasspathResourceURIResolver implements URIResolver { private static class ClasspathResourceURIResolver implements URIResolver {
@@ -445,7 +389,7 @@ public class ZUGFeRDVisualizer {
} }
@Override @Override
public Source resolve(String href, String base) throws TransformerException { public Source resolve(String href, String base) {
return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/" + href)); return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/" + href));
} }
} }

View File

@@ -29,6 +29,7 @@ public class TaxCategoryCodeTypeConstants {
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 = "K"; public static final String INTRACOMMUNITY = "K";
public static final String FREEEXPORT = "G";
public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT).collect(Collectors.toSet()); public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT, FREEEXPORT).collect(Collectors.toSet());
} }

Binary file not shown.

Binary file not shown.

View File

@@ -23,15 +23,35 @@
<renderers> <renderers>
<renderer mime="application/pdf"> <renderer mime="application/pdf">
<fonts><!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed --> <fonts>
<!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed -->
<auto-detect/> <auto-detect/>
<font kerning="no" embed-url="classpath:FreeSans.ttf" embedding-mode="subset"> <font kerning="yes" embed-url="classpath:fonts/SourceSansPro-Regular.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="normal" /> <font-triplet name="SourceSansPro" style="normal" weight="400"/>
<font-triplet name="Times-Bold" style="normal" weight="normal" /> </font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-It.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="italic" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-Bold.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="normal" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-BoldIt.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="italic" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-Regular.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-It.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="italic" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-Bold.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-BoldIt.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="italic" weight="700"/>
</font> </font>
</fonts> </fonts>
<output-profile>classpath:AdobeCompat-v2.icc</output-profile> <output-profile>classpath:AdobeCompat-v2.icc</output-profile>
</renderer> </renderer>
</renderers> </renderers>
</fop> </fop>

View File

@@ -98,6 +98,8 @@
select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ReceivingAdviceReferencedDocument/ram:IssuerAssignedID"/> select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ReceivingAdviceReferencedDocument/ram:IssuerAssignedID"/>
<xsl:apply-templates mode="BT-16" <xsl:apply-templates mode="BT-16"
select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:DespatchAdviceReferencedDocument/ram:IssuerAssignedID"/> select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:DespatchAdviceReferencedDocument/ram:IssuerAssignedID"/>
<xsl:apply-templates mode="BT-X-202"
select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:DeliveryNoteReferencedDocument/ram:IssuerAssignedID"/>
<xsl:apply-templates mode="BT-17" <xsl:apply-templates mode="BT-17"
select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:AdditionalReferencedDocument/ram:IssuerAssignedID[following-sibling::ram:TypeCode='50']"/> select="./rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:AdditionalReferencedDocument/ram:IssuerAssignedID[following-sibling::ram:TypeCode='50']"/>
<xsl:apply-templates mode="BT-18" <xsl:apply-templates mode="BT-18"
@@ -295,6 +297,14 @@
<xr:Receiving_advice_reference> <xr:Receiving_advice_reference>
<xsl:attribute name="xr:id" select="'BT-15'"/> <xsl:attribute name="xr:id" select="'BT-15'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/> <xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:choose>
<xsl:when test="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']">
<xsl:call-template name="dateAsAttribute">
<xsl:with-param name="dateString" select="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']"/>
<xsl:with-param name="attributeName" select="'bt-x-201'"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
<xsl:call-template name="document_reference"/> <xsl:call-template name="document_reference"/>
</xr:Receiving_advice_reference> </xr:Receiving_advice_reference>
</xsl:template> </xsl:template>
@@ -303,9 +313,33 @@
<xr:Despatch_advice_reference> <xr:Despatch_advice_reference>
<xsl:attribute name="xr:id" select="'BT-16'"/> <xsl:attribute name="xr:id" select="'BT-16'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/> <xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:choose>
<xsl:when test="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']">
<xsl:call-template name="dateAsAttribute">
<xsl:with-param name="dateString" select="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']"/>
<xsl:with-param name="attributeName" select="'bt-x-200'"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
<xsl:call-template name="document_reference"/> <xsl:call-template name="document_reference"/>
</xr:Despatch_advice_reference> </xr:Despatch_advice_reference>
</xsl:template> </xsl:template>
<xsl:template mode="BT-X-202"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:DeliveryNoteReferencedDocument/ram:IssuerAssignedID">
<xr:Delivery_note_reference>
<xsl:attribute name="xr:id" select="'BT-X-202'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:choose>
<xsl:when test="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']">
<xsl:call-template name="dateAsAttribute">
<xsl:with-param name="dateString" select="../ram:FormattedIssueDateTime/qdt:DateTimeString[@format = '102']"/>
<xsl:with-param name="attributeName" select="'bt-x-203'"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
<xsl:call-template name="document_reference"/>
</xr:Delivery_note_reference>
</xsl:template>
<xsl:template mode="BT-17" <xsl:template mode="BT-17"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:AdditionalReferencedDocument/ram:IssuerAssignedID[following-sibling::ram:TypeCode='50']"> match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:AdditionalReferencedDocument/ram:IssuerAssignedID[following-sibling::ram:TypeCode='50']">
<xr:Tender_or_lot_reference> <xr:Tender_or_lot_reference>
@@ -2437,6 +2471,7 @@
<xsl:template name="text"> <xsl:template name="text">
<xsl:value-of select="."/> <xsl:value-of select="."/>
</xsl:template> </xsl:template>
<xsl:template name="date"> <xsl:template name="date">
<xsl:variable name="normalizeddate" select="normalize-space(replace(., '-', ''))"/> <xsl:variable name="normalizeddate" select="normalize-space(replace(., '-', ''))"/>
<xsl:choose> <xsl:choose>
@@ -2453,6 +2488,24 @@
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
<xsl:template name="dateAsAttribute">
<xsl:param name="dateString"/>
<xsl:param name="attributeName" select="'date'"/>
<xsl:variable name="normalizeddate" select="normalize-space(replace($dateString, '-', ''))"/>
<xsl:choose>
<xsl:when test="matches($normalizeddate, '^[0-9]{8}$')">
<xsl:attribute name="{$attributeName}"
select="xs:date( concat(substring($normalizeddate,1,4), '-', substring($normalizeddate,5,2), '-', substring($normalizeddate,7,2) ) )"/>
</xsl:when>
<xsl:otherwise>ILLEGAL DATE FORMAT: &lt;para&gt;Mit diesem Datentyp wird ein kalendarisches Datum
abgebildet, wie es in der ISO 8601 Spezifikation &lt;quote&gt;Calendar date complete representation&lt;/quote&gt;
beschrieben ist (siehe ISO 8601:2004, Abschnitt 5.2.1.1). Das Datum beinhaltet keine Zeitangabe. Das
konkret zu verwendende Format ist abhängig von der genutzten Syntax.&lt;/para&gt;
&lt;para&gt;Der Datentyp basiert auf dem Typ &lt;quote&gt;Date Time. Type&lt;/quote&gt;, wie in ISO
15000-5:2014 Anhang B definiert.&lt;/para&gt;
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="identifier-with-scheme-and-version"> <xsl:template name="identifier-with-scheme-and-version">
<xsl:param name="schemeID" as="element()?"/> <xsl:param name="schemeID" as="element()?"/>
<xsl:if test="@listID | @schemeID"> <xsl:if test="@listID | @schemeID">

View File

@@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:axf="http://www.antennahouse.com/names/XSL/Extensions"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<!-- Imports -->
<xsl:import href="common-xr.xsl"/>
<xsl:import href="xr-pdf/lib/konstanten.xsl"/>
<!--
FO engine used can be specified.
Specific extensions will be then enabled.
Supported values are:
axf - Antenna House XSL Formatter
fop - Apache FOP
-->
<xsl:param name="foengine"/>
<xsl:param name="axf.extensions"
select="if ($foengine eq 'axf') then true() else false()"/>
<xsl:param name="fop.extensions"
select="if ($foengine eq 'fop') then true() else false()"/>
<xsl:variable name="xml_result_color">
<xsl:if test="/validation/xml/summary/@status = 'valid'">
<xsl:text>green</xsl:text>
</xsl:if>
<xsl:if test="/validation/xml/summary/@status = 'invalid'">
<xsl:text>red</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:variable name="pdf_result_color">
<xsl:if test="/validation/pdf/summary/@status = 'valid'">
<xsl:text>green</xsl:text>
</xsl:if>
<xsl:if test="/validation/pdf/summary/@status = 'invalid'">
<xsl:text>red</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:variable name="pdf_result_text">
<xsl:if test="/validation/xml/summary/@status = 'valid'">
<xsl:text>Das ZUGFeRD-PDF ist valide.</xsl:text>
</xsl:if>
<xsl:if test="/validation/xml/summary/@status = 'invalid'">
<xsl:text>Das ZUGFeRD-PDF ist nicht valide.</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:variable name="result_text">
<xsl:if test="/validation/xml/summary/@status = 'valid'">
<xsl:text>Es wird empfohlen, das Dokument anzunehmen und weiterzuverarbeiten.</xsl:text>
</xsl:if>
<xsl:if test="/validation/xml/summary/@status = 'invalid'">
<xsl:text>Es wird empfohlen, das Dokument zurückzuweisen.</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:template match="validation">
<fo:root language="{$lang}"
font-family="{$fontSans}">
<fo:layout-master-set>
<fo:simple-page-master master-name="DIN-A4"
page-height="297mm"
page-width="210mm">
<fo:region-body region-name="body"
margin="20mm 10mm 20mm 20mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="DIN-A4">
<fo:flow flow-name="body">
<fo:block font-size="24px"
font-weight="bold">
<xsl:text>Prüfbericht</xsl:text>
</fo:block>
<xsl:call-template name="SubHeader">
<xsl:with-param name="text"
select="&#34;Angaben zum geprüften Dokument&#34;"/>
<xsl:with-param name="color"
select="&#34;black&#34;"/>
</xsl:call-template>
<fo:block>
<fo:block text-align="justify">
<fo:float float="right">
<fo:block>
<xsl:value-of select="/validation/@filename"/>
</fo:block>
</fo:float>
<xsl:text>Referenz:</xsl:text>
</fo:block>
</fo:block>
<fo:block>
<fo:block text-align="justify">
<fo:float float="right">
<fo:block>
<xsl:value-of select="/validation/@datetime"/>
</fo:block>
</fo:float>
<xsl:text>Zeitpunkt der Prüfung:</xsl:text>
</fo:block>
</fo:block>
<fo:block>
<fo:block text-align="justify">
<fo:float float="right">
<fo:block>
<xsl:value-of select="/validation/xml/info/profile"/>
</fo:block>
</fo:float>
<xsl:text>Erkannter Dokumenttyp:</xsl:text>
</fo:block>
</fo:block>
<xsl:apply-templates select="/validation/pdf"/>
<!--
<xsl:call-template name="SubHeader">
<xsl:with-param name="text"
select='"Konformitätsprüfung:"'/>
<xsl:with-param name="color"
select='"black"'/>
</xsl:call-template>
-->
<xsl:call-template name="SubHeader">
<xsl:with-param name="text"
select="concat('Bewertung: ', $result_text)"/>
<xsl:with-param name="color"
select="$xml_result_color"/>
</xsl:call-template>
<fo:block>Validierungsergebnisse im Detail:</fo:block>
<fo:table>
<fo:table-column border-style="solid"/>
<fo:table-column border-style="solid"/>
<fo:table-column border-style="solid"/>
<fo:table-column column-width="70%"
border-style="solid"/>
<fo:table-header>
<fo:table-row background-color="#E0E0E0"
font-weight="bold">
<fo:table-cell>
<fo:block>Type</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Code</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Schwere</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Text</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:choose>
<xsl:when test="/validation/xml/messages">
<xsl:apply-templates select="/validation/xml/messages"/>
</xsl:when>
<xsl:otherwise>
<fo:table-row font-size="12px"
border-style="solid">
<fo:table-cell number-columns-spanned="4">
<fo:block text-align="center">Es gibt weder Hinweise noch Fehler.</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:otherwise>
</xsl:choose>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="notice|error">
<fo:table-row font-size="12px"
border-style="solid">
<fo:table-cell number-rows-spanned="2">
<fo:block>
<xsl:value-of select="@type"/>
</fo:block>
</fo:table-cell>
<fo:table-cell number-rows-spanned="2">
<fo:block>
<xsl:call-template name="SUBID">
<xsl:with-param name="myparam"
select="substring-after(.,' [ID ')"/>
</xsl:call-template>
</fo:block>
</fo:table-cell>
<fo:table-cell number-rows-spanned="2">
<fo:block>
<xsl:choose>
<xsl:when test="name() = 'error'">
<xsl:attribute name="color">red</xsl:attribute>
<xsl:text>Fehler</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Hinweis</xsl:text>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
<xsl:if test="name() = 'error'">
<xsl:attribute name="color">red</xsl:attribute>
</xsl:if>
<xsl:value-of select="substring-before(.,' [ID')"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row font-size="10px"
border-style="solid">
<fo:table-cell>
<fo:block>
<xsl:if test="name() = 'error'">
<xsl:attribute name="color">red</xsl:attribute>
</xsl:if>
<xsl:value-of select="concat('Pfad: ', @location)"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="pdf">
<xsl:call-template name="SubHeader">
<xsl:with-param name="text"
select="concat('ZUGFeRD-PDF: ', $pdf_result_text)"/>
<xsl:with-param name="color"
select="&#34;black&#34;"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="SubHeader">
<xsl:param name="text"/>
<xsl:param name="color"/>
<fo:block color="{$color}"
background-color="#E0E0E0"
margin-bottom="10px"
padding="3px"
font-weight="bold"
margin-top="10px">
<xsl:value-of select="$text"/>
</fo:block>
</xsl:template>
<xsl:template name="SUBID">
<xsl:param name="myparam"/>
<xsl:variable name="myparam.end"
select="substring-before($myparam, ']')"/>
<xsl:value-of select="$myparam.end"/>
</xsl:template>
</xsl:stylesheet>

View File

@@ -12,7 +12,7 @@
== Schriften == Schriften
=========================================================================== --> =========================================================================== -->
<xsl:variable name="fontSans">SourceSerifPro</xsl:variable> <xsl:variable name="fontSans">SourceSansPro</xsl:variable>
<xsl:variable name="fontSerif">SourceSerifPro</xsl:variable> <xsl:variable name="fontSerif">SourceSerifPro</xsl:variable>
<xsl:variable name="amount-picture" select="xrf:_('amount-format')"/> <xsl:variable name="amount-picture" select="xrf:_('amount-format')"/>

View File

@@ -11,6 +11,7 @@
<xsl:variable name="i18n.history" select="'Bearbeitungshistorie'"/> <xsl:variable name="i18n.history" select="'Bearbeitungshistorie'"/>
<xsl:variable name="i18n.disclaimer" select="'Wir übernehmen keine Haftung für die Richtigkeit der Daten.'"/> <xsl:variable name="i18n.disclaimer" select="'Wir übernehmen keine Haftung für die Richtigkeit der Daten.'"/>
<xsl:variable name="i18n.recipientInfo" select="'Informationen zum Käufer'"/> <xsl:variable name="i18n.recipientInfo" select="'Informationen zum Käufer'"/>
<xsl:variable name="i18n.dateOf" select="' vom '"/>
<xsl:variable name="i18n.bt50" select="'Straße / Haus-Nr.'"/> <xsl:variable name="i18n.bt50" select="'Straße / Haus-Nr.'"/>
<xsl:variable name="i18n.bt51" select="'Postfach'"/> <xsl:variable name="i18n.bt51" select="'Postfach'"/>
<xsl:variable name="i18n.bt163" select="'Adresszusatz'"/> <xsl:variable name="i18n.bt163" select="'Adresszusatz'"/>
@@ -197,6 +198,7 @@
<xsl:variable name="i18n.bt17" select="'Vergabenummer'"/> <xsl:variable name="i18n.bt17" select="'Vergabenummer'"/>
<xsl:variable name="i18n.bt15" select="'Kennung der Empfangsbestätigung'"/> <xsl:variable name="i18n.bt15" select="'Kennung der Empfangsbestätigung'"/>
<xsl:variable name="i18n.bt16" select="'Kennung der Versandanzeige'"/> <xsl:variable name="i18n.bt16" select="'Kennung der Versandanzeige'"/>
<xsl:variable name="i18n.btx202" select="'Kennung des Lieferscheins'"/>
<xsl:variable name="i18n.bt23" select="'Prozesskennung'"/> <xsl:variable name="i18n.bt23" select="'Prozesskennung'"/>
<xsl:variable name="i18n.bt24" select="'Spezifikationskennung'"/> <xsl:variable name="i18n.bt24" select="'Spezifikationskennung'"/>
<xsl:variable name="i18n.bt18" select="'Objektkennung'"/> <xsl:variable name="i18n.bt18" select="'Objektkennung'"/>

View File

@@ -11,6 +11,7 @@
<xsl:variable name="i18n.history" select="'History'"/> <xsl:variable name="i18n.history" select="'History'"/>
<xsl:variable name="i18n.disclaimer" select="'We accept no liability for the correctness of the data'"/> <xsl:variable name="i18n.disclaimer" select="'We accept no liability for the correctness of the data'"/>
<xsl:variable name="i18n.recipientInfo" select="'Buyer Information'"/> <xsl:variable name="i18n.recipientInfo" select="'Buyer Information'"/>
<xsl:variable name="i18n.dateOf" select="' from '"/>
<xsl:variable name="i18n.bt50" select="'Street / house number'"/> <xsl:variable name="i18n.bt50" select="'Street / house number'"/>
<xsl:variable name="i18n.bt51" select="'PO Box'"/> <xsl:variable name="i18n.bt51" select="'PO Box'"/>
<xsl:variable name="i18n.bt163" select="'Address Addition'"/> <xsl:variable name="i18n.bt163" select="'Address Addition'"/>
@@ -197,6 +198,7 @@
<xsl:variable name="i18n.bt17" select="'Assignment number'"/> <xsl:variable name="i18n.bt17" select="'Assignment number'"/>
<xsl:variable name="i18n.bt15" select="'Receipt confirmation ID'"/> <xsl:variable name="i18n.bt15" select="'Receipt confirmation ID'"/>
<xsl:variable name="i18n.bt16" select="'Dispatch note ID'"/> <xsl:variable name="i18n.bt16" select="'Dispatch note ID'"/>
<xsl:variable name="i18n.btx202" select="'Delivery note ID'"/>
<xsl:variable name="i18n.bt23" select="'Process ID'"/> <xsl:variable name="i18n.bt23" select="'Process ID'"/>
<xsl:variable name="i18n.bt24" select="'Specification ID'"/> <xsl:variable name="i18n.bt24" select="'Specification ID'"/>
<xsl:variable name="i18n.bt18" select="'Object ID'"/> <xsl:variable name="i18n.bt18" select="'Object ID'"/>

View File

@@ -11,6 +11,7 @@
<xsl:variable name="i18n.history" select="'Historique de traitement'"/> <xsl:variable name="i18n.history" select="'Historique de traitement'"/>
<xsl:variable name="i18n.disclaimer" select="'Nous n''assumons aucune responsabilité quant à l''exactitude des données.'"/> <xsl:variable name="i18n.disclaimer" select="'Nous n''assumons aucune responsabilité quant à l''exactitude des données.'"/>
<xsl:variable name="i18n.recipientInfo" select="'Informations sur l''acheteur'"/> <xsl:variable name="i18n.recipientInfo" select="'Informations sur l''acheteur'"/>
<xsl:variable name="i18n.dateOf" select="' du '"/>
<xsl:variable name="i18n.bt50" select="'Rue / Numéro de maison'"/> <xsl:variable name="i18n.bt50" select="'Rue / Numéro de maison'"/>
<xsl:variable name="i18n.bt51" select="'Boîte postale'"/> <xsl:variable name="i18n.bt51" select="'Boîte postale'"/>
<xsl:variable name="i18n.bt163" select="'Supplément d''adresse'"/> <xsl:variable name="i18n.bt163" select="'Supplément d''adresse'"/>
@@ -197,6 +198,7 @@
<xsl:variable name="i18n.bt17" select="'Numéro d''attribution'"/> <xsl:variable name="i18n.bt17" select="'Numéro d''attribution'"/>
<xsl:variable name="i18n.bt15" select="'Identifiant de l''accusé de réception'"/> <xsl:variable name="i18n.bt15" select="'Identifiant de l''accusé de réception'"/>
<xsl:variable name="i18n.bt16" select="'Identifiant du bordereau d''expédition'"/> <xsl:variable name="i18n.bt16" select="'Identifiant du bordereau d''expédition'"/>
<xsl:variable name="i18n.btx202" select="'Identifiant du bon de livraison'"/>
<xsl:variable name="i18n.bt23" select="'Identifiant processus'"/> <xsl:variable name="i18n.bt23" select="'Identifiant processus'"/>
<xsl:variable name="i18n.bt24" select="'Identifiant spécification'"/> <xsl:variable name="i18n.bt24" select="'Identifiant spécification'"/>
<xsl:variable name="i18n.bt18" select="'Identifiant objet'"/> <xsl:variable name="i18n.bt18" select="'Identifiant objet'"/>

View File

@@ -1144,17 +1144,29 @@ function downloadData (element_id) {
<xsl:apply-templates select="./xr:VAT_BREAKDOWN"/> <xsl:apply-templates select="./xr:VAT_BREAKDOWN"/>
</div> </div>
<xsl:choose>
<xsl:when test="./xr:DOCUMENT_LEVEL_ALLOWANCES">
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig">
<xsl:apply-templates select="./xr:DOCUMENT_LEVEL_ALLOWANCES"/> <xsl:apply-templates select="./xr:DOCUMENT_LEVEL_ALLOWANCES"/>
</div> </div>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="./xr:DOCUMENT_LEVEL_CHARGES">
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig">
<xsl:apply-templates select="./xr:DOCUMENT_LEVEL_CHARGES"/> <xsl:apply-templates select="./xr:DOCUMENT_LEVEL_CHARGES"/>
</div> </div>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="./xr:LOGISTICS_SERVICE_CHARGES">
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig">
<xsl:apply-templates select="./xr:LOGISTICS_SERVICE_CHARGES"/> <xsl:apply-templates select="./xr:LOGISTICS_SERVICE_CHARGES"/>
</div> </div>
</xsl:when>
</xsl:choose>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig first"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig first">
<div class="boxzeile"> <div class="boxzeile">
@@ -2182,11 +2194,24 @@ function downloadData (element_id) {
</div> </div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende"><xsl:value-of select="$i18n.bt15"/>:</div> <div class="boxdaten legende"><xsl:value-of select="$i18n.bt15"/>:</div>
<div id="BT-15" title="BT-15" class="boxdaten wert"><xsl:value-of select="xr:Receiving_advice_reference"/></div> <div id="BT-15" title="BT-15" class="boxdaten wert">
<xsl:value-of select="xr:Receiving_advice_reference"/>
<xsl:if test="xr:Receiving_advice_reference/@bt-x-201"><xsl:value-of select="concat($i18n.dateOf, format-date(xr:Receiving_advice_reference/@bt-x-201,'[D].[M].[Y]'))"/></xsl:if>
</div>
</div> </div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende"><xsl:value-of select="$i18n.bt16"/>:</div> <div class="boxdaten legende"><xsl:value-of select="$i18n.bt16"/>:</div>
<div id="BT-16" title="BT-16" class="boxdaten wert"><xsl:value-of select="xr:Despatch_advice_reference"/></div> <div id="BT-16" title="BT-16" class="boxdaten wert">
<xsl:value-of select="xr:Despatch_advice_reference"/>
<xsl:if test="xr:Despatch_advice_reference/@bt-x-200"><xsl:value-of select="concat($i18n.dateOf, format-date(xr:Despatch_advice_reference/@bt-x-200,'[D].[M].[Y]'))"/></xsl:if>
</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende"><xsl:value-of select="$i18n.btx202"/>:</div>
<div id="BT-X-202" title="BT-X-202" class="boxdaten wert">
<xsl:value-of select="xr:Delivery_note_reference"/>
<xsl:if test="xr:Delivery_note_reference/@bt-x-203"><xsl:value-of select="concat($i18n.dateOf, format-date(xr:Delivery_note_reference/@bt-x-203,'[D].[M].[Y]'))"/></xsl:if>
</div>
</div> </div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende"><xsl:value-of select="$i18n.bt23"/>:</div> <div class="boxdaten legende"><xsl:value-of select="$i18n.bt23"/>:</div>

View File

@@ -166,6 +166,118 @@ public class DeSerializationTest extends ResourceCase {
} }
public void testDeserializedFiles() {
File inputUBL = getResourceAsFile("XRECHNUNG_Elektron.ubl.xml");
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
zii.doIgnoreCalculationErrors();
Invoice i = new Invoice();
String exText = null;
try {
zii.fromXML(new String(Files.readAllBytes(inputUBL.toPath()), StandardCharsets.UTF_8));
ObjectMapper mapper = new ObjectMapper();
zii.extractInto(i);
String json = mapper.writeValueAsString(i);
Invoice newInvoiceFromJSON = mapper.readValue(json, Invoice.class);
assertEquals("181301674", i.getNumber());
assertEquals(newInvoiceFromJSON.getNumber(), i.getNumber());
assertEquals(newInvoiceFromJSON.getAdditionalReferencedDocuments()[0].getFilename(), i.getAdditionalReferencedDocuments()[0].getFilename());
assertEquals(newInvoiceFromJSON.getAdditionalReferencedDocuments().length, 2);
} catch (IOException e) {
exText = e.getMessage();
} catch (XPathExpressionException e) {
exText = e.getMessage();
} catch (ParseException e) {
exText = e.getMessage();
}
assertNull(exText);
}
public void testFileSerialization() {
String base64 = "b25ldHdvdGhyZWU=";
String json = "{\n" +
" \"additionalReferencedDocuments\": [\n" +
" {\n" +
" \"data\": \"" + base64 + "\",\n" +
" \"description\": \"Additional file attachment\",\n" +
" \"filename\": \"text.txt\",\n" +
" \"mimetype\": \"text/plain\",\n" +
" \"relation\": \"Data\"\n" +
" }\n" +
"],\n" +
"\n" +
" \"number\": \"471102\",\n" +
" \"currency\": \"EUR\",\n" +
" \"issueDate\": \"2018-03-04T00:00:00.000+01:00\",\n" +
" \"dueDate\": \"2018-03-04T00:00:00.000+01:00\",\n" +
" \"deliveryDate\": \"2018-03-04T00:00:00.000+01:00\",\n" +
" \"sender\": {\n" +
" \"name\": \"Lieferant GmbH\",\n" +
" \"zip\": \"80333\",\n" +
" \"street\": \"Lieferantenstraße 20\",\n" +
" \"location\": \"München\",\n" +
" \"country\": \"DE\",\n" +
" \"taxID\": \"201/113/40209\",\n" +
" \"vatID\": \"DE123456789\",\n" +
" \"globalID\": \"4000001123452\",\n" +
" \"globalIDScheme\": \"0088\"\n" +
" },\n" +
" \"recipient\": {\n" +
" \"name\": \"Kunden AG Mitte\",\n" +
" \"zip\": \"69876\",\n" +
" \"street\": \"Kundenstraße 15\",\n" +
" \"location\": \"Frankfurt\",\n" +
" \"country\": \"DE\"\n" +
" },\n" +
" \"zfitems\": [\n" +
" {\n" +
" \"price\": 9.9,\n" +
" \"quantity\": 20,\n" +
" \"product\": {\n" +
" \"unit\": \"H87\",\n" +
" \"name\": \"Trennblätter A4\",\n" +
" \"description\": \"\",\n" +
" \"vatpercent\": 19,\n" +
" \"taxCategoryCode\": \"S\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"price\": 5.5,\n" +
" \"quantity\": 50,\n" +
" \"product\": {\n" +
" \"unit\": \"H87\",\n" +
" \"name\": \"Joghurt Banane\",\n" +
" \"description\": \"\",\n" +
" \"vatpercent\": 7,\n" +
" \"taxCategoryCode\": \"S\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}\n";
ObjectMapper mapper = new ObjectMapper();
try {
Invoice newInvoiceFromJSON = mapper.readValue(json, Invoice.class);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(newInvoiceFromJSON);
String theXML = new String(zf2p.getXML());
assertTrue(theXML.contains("<udt:DateTimeString format=\"102\">20180304</udt:DateTimeString>"));
assertTrue(theXML.contains(base64));
} catch (Exception e) {
fail("No exception expected");
}
}
public void testFull300Roundtrip() { public void testFull300Roundtrip() {
File inputCII = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.cii.xml"); File inputCII = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.cii.xml");

View File

@@ -22,10 +22,9 @@ package org.mustangproject.ZUGFeRD;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.mustangproject.ZUGFeRD.ZUGFeRDVisualizer.Language;
import org.mustangproject.util.ByteArraySearcher; import org.mustangproject.util.ByteArraySearcher;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -40,155 +39,34 @@ public class VisualizationTest extends ResourceCase {
final String TARGET_PDF_UBL = "./target/testout-Visualization-cii.pdf"; final String TARGET_PDF_UBL = "./target/testout-Visualization-cii.pdf";
public void testCIIVisualizationBasic() { public void testCIIVisualizationBasic() {
this.runZUGFeRDVisualization("factur-x.xml", "factur-x-vis.fr.html", Language.FR);
// the writing part
String sourceFilename = "factur-x.xml";
File CIIinputFile = getResourceAsFile(sourceFilename);
String expected = null;
String result = null;
try {
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
result = zvi.visualize(CIIinputFile.getAbsolutePath(), ZUGFeRDVisualizer.Language.FR)
.replace("\r", "")
.replace("\n", "")
.replace("\t", "")
.replace(" ", "");
File expectedResult = getResourceAsFile("factur-x-vis.fr.html");
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8)
.replace("\r", "")
.replace("\n", "")
.replace("\t", "")
.replace(" ", "");
// remove linebreaks as well...
} catch (UnsupportedOperationException e) {
fail("UnsupportedOperationException should not happen: " + e.getMessage());
} catch (IllegalArgumentException e) {
fail("IllegalArgumentException should not happen: " + e.getMessage());
} catch (TransformerException e) {
fail("TransformerException should not happen: " + e.getMessage());
} catch (IOException e) {
fail("IOException should not happen: " + e.getMessage());
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: " + e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: " + e.getMessage());
}
assertNotNull(result);
// Reading ZUGFeRD
assertEquals(expected, result);
} }
public void testCIIVisualizationExtended() { public void testCIIVisualizationExtended() {
this.runZUGFeRDVisualization("factur-x-extended.xml", "factur-x-vis-extended.de.html", Language.DE);
// the writing part
String sourceFilename = "factur-x-extended.xml";
File CIIinputFile = getResourceAsFile(sourceFilename);
String expected = null;
String result = null;
try {
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
result = zvi.visualize(CIIinputFile.getAbsolutePath(), ZUGFeRDVisualizer.Language.DE).replace("\r", "").replace("\n", "")
.replace("\t", "")
.replace(" ", "");
File expectedResult = getResourceAsFile("factur-x-vis-extended.de.html");
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r", "").replace("\n", "")
.replace("\t", "")
.replace(" ", "");
// remove linebreaks as well...
} catch (UnsupportedOperationException e) {
fail("UnsupportedOperationException should not happen: " + e.getMessage());
} catch (IllegalArgumentException e) {
fail("IllegalArgumentException should not happen: " + e.getMessage());
} catch (TransformerException e) {
fail("TransformerException should not happen: " + e.getMessage());
} catch (IOException e) {
fail("IOException should not happen: " + e.getMessage());
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: " + e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: " + e.getMessage());
}
assertNotNull(result);
// Reading ZUGFeRD
assertEquals(expected, result);
} }
public void testUBLCreditNoteVisualizationBasic() { public void testUBLCreditNoteVisualizationBasic() {
this.runZUGFeRDVisualization("ubl-creditnote.xml", "factur-x-vis-ubl-creditnote.en.html", Language.EN);
// the writing part
File UBLinputFile = getResourceAsFile("ubl-creditnote.xml");
String expected = null;
String result = null;
try {
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
result = zvi.visualize(UBLinputFile.getAbsolutePath(), ZUGFeRDVisualizer.Language.EN).replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "");
File expectedResult = getResourceAsFile("factur-x-vis-ubl-creditnote.en.html");
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "");
// remove linebreaks as well...
} catch (UnsupportedOperationException e) {
fail("UnsupportedOperationException should not happen: " + e.getMessage());
} catch (IllegalArgumentException e) {
fail("IllegalArgumentException should not happen: " + e.getMessage());
} catch (TransformerException e) {
fail("TransformerException should not happen: " + e.getMessage());
} catch (IOException e) {
fail("IOException should not happen: " + e.getMessage());
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: " + e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: " + e.getMessage());
} }
assertNotNull(result);
// Reading ZUGFeRD
assertEquals(expected, result);
}
public void testUBLVisualizationBasic() { public void testUBLVisualizationBasic() {
this.runZUGFeRDVisualization("ubl/01.01a-INVOICE.ubl.xml", "factur-x-vis-ubl.en.html", Language.EN);
}
// the writing part private void runZUGFeRDVisualization(String inputFilename, String resultFileName, Language lang) {
File UBLinputFile = getResourceAsFile("ubl/01.01a-INVOICE.ubl.xml"); File CIIinputFile = getResourceAsFile(inputFilename);
String expected = null; String expected = null;
String result = null; String result = null;
try { try {
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer(); ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
/* remove file endings so that tests can also pass after checking result = zvi.visualize(CIIinputFile.getAbsolutePath(), lang);
out from git with arbitrary options (which may include CSRF changes) Files.write(Paths.get("./target/testout-" + resultFileName), result.getBytes(StandardCharsets.UTF_8));
*/
result = zvi.visualize(UBLinputFile.getAbsolutePath(), ZUGFeRDVisualizer.Language.EN).replace("\r", "").replace("\n", "")
.replace("\t", "")
.replace(" ", "");
File expectedResult = getResourceAsFile("factur-x-vis-ubl.en.html"); File expectedResult = getResourceAsFile(resultFileName);
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r", "").replace("\n", "") expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8)
.replace("\t", "") ;
.replace(" ", "");
// remove linebreaks as well...
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
fail("UnsupportedOperationException should not happen: " + e.getMessage()); fail("UnsupportedOperationException should not happen: " + e.getMessage());
@@ -198,16 +76,20 @@ public class VisualizationTest extends ResourceCase {
fail("TransformerException should not happen: " + e.getMessage()); fail("TransformerException should not happen: " + e.getMessage());
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not happen: " + e.getMessage()); fail("IOException should not happen: " + e.getMessage());
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: " + e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: " + e.getMessage());
} }
assertNotNull(result); assertNotNull(result);
// Reading ZUGFeRD /* remove file endings so that tests can also pass after checking
assertEquals(expected, result); out from git with arbitrary options (which may include CSRF changes)
*/
assertEquals(expected.replace("\r", "")
.replace("\n", "")
.replace("\t", "")
.replace(" ", ""), result.replace("\r", "")
.replace("\n", "")
.replace("\t", "")
.replace(" ", ""));
} }
public void testPDFVisualizationCII() { public void testPDFVisualizationCII() {

View File

@@ -261,7 +261,7 @@ public class ZF2PushTest extends TestCase {
.setNumber(number) .setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1")))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1"))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("AK"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1")))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1"))))
); );
@@ -542,7 +542,7 @@ public class ZF2PushTest extends TestCase {
.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)))
.addCashDiscount(new CashDiscount(new BigDecimal(2), 14)) .addCashDiscount(new CashDiscount(new BigDecimal(2), 14))
.setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE) .setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
.setInvoiceReferencedDocumentID("abc123") .setInvoiceReferencedDocumentID("abc123").addInvoiceReferencedDocument(new ReferencedDocument("abcd1234"))
); );
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
@@ -587,6 +587,8 @@ public class ZF2PushTest extends TestCase {
Invoice i = zii.extractInvoice(); Invoice i = zii.extractInvoice();
assertEquals("abc123", i.getInvoiceReferencedDocumentID()); assertEquals("abc123", i.getInvoiceReferencedDocumentID());
assertEquals(1, i.getInvoiceReferencedDocuments().size());
assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID());
assertEquals("4304171000002", i.getRecipient().getGlobalID()); assertEquals("4304171000002", i.getRecipient().getGlobalID());
assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID()); assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID());
assertEquals(orgID, i.getSender().getID()); assertEquals(orgID, i.getSender().getID());
@@ -803,24 +805,5 @@ public class ZF2PushTest extends TestCase {
match the read grand total */ match the read grand total */
} }
} }
public void testRead() {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("\\Users\\jstaerk\\temp\\1424413_anonymized.xml");
try {
Invoice i = zii.extractInvoice();
TransactionCalculator tc=new TransactionCalculator(i);
assertEquals(0,tc.getGrandTotal().compareTo(new BigDecimal("442.83")));
} catch (XPathExpressionException e) {
fail("XPathExpressionException should not be raised");
} catch (ParseException e) {
fail("ParseException should not be raised");
/* a parseException would also be fired if the calculated grand total does not
match the read grand total */
}
}
} }

View File

@@ -466,4 +466,12 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
assertFalse(invoice.getZFItems()[0].getNotes() == null); assertFalse(invoice.getZFItems()[0].getNotes() == null);
assertEquals(1, invoice.getZFItems()[0].getNotes().length); assertEquals(1, invoice.getZFItems()[0].getNotes().length);
} }
@Test
public void testImportXRechnungWithoutCalculationErrors() throws FileNotFoundException, XPathExpressionException, ParseException {
File inputFile = getResourceAsFile("cii/02.03a-INVOICE_uncefact.xml");
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(inputFile));
assertEquals("0", zii.importedInvoice.getDuePayable().toPlainString());
}
} }

File diff suppressed because one or more lines are too long

View File

@@ -359,6 +359,9 @@ GLN 4304171000002
</ram:ActualDeliverySupplyChainEvent> </ram:ActualDeliverySupplyChainEvent>
<ram:DeliveryNoteReferencedDocument> <ram:DeliveryNoteReferencedDocument>
<ram:IssuerAssignedID>L87654321012</ram:IssuerAssignedID> <ram:IssuerAssignedID>L87654321012</ram:IssuerAssignedID>
<ram:FormattedIssueDateTime>
<qdt:DateTimeString format="102">20241031</qdt:DateTimeString>
</ram:FormattedIssueDateTime>
</ram:DeliveryNoteReferencedDocument> </ram:DeliveryNoteReferencedDocument>
</ram:ApplicableHeaderTradeDelivery> </ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement> <ram:ApplicableHeaderTradeSettlement>

View File

@@ -1085,7 +1085,6 @@
</div> </div>
<div class="boxabstand"></div> <div class="boxabstand"></div>
</div> </div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig">
<div class="boxzeile"> <div class="boxzeile">
<div id="uebersichtVersandkosten" class="box"> <div id="uebersichtVersandkosten" class="box">
@@ -2248,6 +2247,10 @@
<div class="boxdaten legende">Kennung der Versandanzeige:</div> <div class="boxdaten legende">Kennung der Versandanzeige:</div>
<div id="BT-16" title="BT-16" class="boxdaten wert"></div> <div id="BT-16" title="BT-16" class="boxdaten wert"></div>
</div> </div>
<div class="boxzeile">
<div class="boxdaten legende">Kennung des Lieferscheins:</div>
<div id="BT-X-202" title="BT-X-202" class="boxdaten wert">L87654321012 vom 31.10.2024</div>
</div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende">Prozesskennung:</div> <div class="boxdaten legende">Prozesskennung:</div>
<div id="BT-23" title="BT-23" class="boxdaten wert"></div> <div id="BT-23" title="BT-23" class="boxdaten wert"></div>

View File

@@ -1359,7 +1359,6 @@
</div> </div>
<div class="boxabstand"></div> <div class="boxabstand"></div>
</div> </div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig first"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig first">
<div class="boxzeile"> <div class="boxzeile">
<div id="uebersichtZahlungsinformationen" class="box subBox"> <div id="uebersichtZahlungsinformationen" class="box subBox">
@@ -2355,6 +2354,10 @@
<div class="boxdaten legende">Dispatch note ID:</div> <div class="boxdaten legende">Dispatch note ID:</div>
<div id="BT-16" title="BT-16" class="boxdaten wert"></div> <div id="BT-16" title="BT-16" class="boxdaten wert"></div>
</div> </div>
<div class="boxzeile">
<div class="boxdaten legende">Delivery note ID:</div>
<div id="BT-X-202" title="BT-X-202" class="boxdaten wert"></div>
</div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende">Process ID:</div> <div class="boxdaten legende">Process ID:</div>
<div id="BT-23" title="BT-23" class="boxdaten wert"></div> <div id="BT-23" title="BT-23" class="boxdaten wert"></div>

View File

@@ -1219,9 +1219,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig first"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig first">
<div class="boxzeile"> <div class="boxzeile">
<div id="uebersichtZahlungsinformationen" class="box subBox"> <div id="uebersichtZahlungsinformationen" class="box subBox">
@@ -1755,6 +1752,10 @@
<div class="boxdaten legende">Dispatch note ID:</div> <div class="boxdaten legende">Dispatch note ID:</div>
<div id="BT-16" title="BT-16" class="boxdaten wert"></div> <div id="BT-16" title="BT-16" class="boxdaten wert"></div>
</div> </div>
<div class="boxzeile">
<div class="boxdaten legende">Delivery note ID:</div>
<div id="BT-X-202" title="BT-X-202" class="boxdaten wert"></div>
</div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende">Process ID:</div> <div class="boxdaten legende">Process ID:</div>
<div id="BT-23" title="BT-23" class="boxdaten wert"></div> <div id="BT-23" title="BT-23" class="boxdaten wert"></div>

View File

@@ -1250,9 +1250,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig"></div>
<div class="boxtabelle boxabstandtop boxtabelleZweispaltig first"> <div class="boxtabelle boxabstandtop boxtabelleZweispaltig first">
<div class="boxzeile"> <div class="boxzeile">
<div id="uebersichtZahlungsinformationen" class="box subBox"> <div id="uebersichtZahlungsinformationen" class="box subBox">
@@ -1927,6 +1924,10 @@
<div class="boxdaten legende">Identifiant du bordereau d'expédition:</div> <div class="boxdaten legende">Identifiant du bordereau d'expédition:</div>
<div id="BT-16" title="BT-16" class="boxdaten wert"></div> <div id="BT-16" title="BT-16" class="boxdaten wert"></div>
</div> </div>
<div class="boxzeile">
<div class="boxdaten legende">Identifiant du bon de livraison:</div>
<div id="BT-X-202" title="BT-X-202" class="boxdaten wert"></div>
</div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende">Identifiant processus:</div> <div class="boxdaten legende">Identifiant processus:</div>
<div id="BT-23" title="BT-23" class="boxdaten wert"></div> <div id="BT-23" title="BT-23" class="boxdaten wert"></div>

View File

@@ -3,8 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version> <packaging>pom</packaging>
<packaging>pom</packaging>
<name>Mustang</name> <name>Mustang</name>
<modules> <modules>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
@@ -11,7 +11,7 @@
<name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name> <name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
<repositories> <repositories>
<repository> <repository>
<!-- for jargs --> <!-- for jargs -->
@@ -38,7 +38,7 @@
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>library</artifactId> <artifactId>library</artifactId>
<version>2.15.2-SNAPSHOT</version> <version>2.15.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dom4j</groupId> <groupId>org.dom4j</groupId>