Merge pull request #252 from weclapp-dev/master
Add a document type and additional fields
This commit is contained in:
@@ -46,13 +46,18 @@ public class Invoice implements IExportableTransaction {
|
|||||||
protected Date detailedDeliveryDateStart = null;
|
protected Date detailedDeliveryDateStart = null;
|
||||||
protected Date detailedDeliveryPeriodEnd = null;
|
protected Date detailedDeliveryPeriodEnd = null;
|
||||||
|
|
||||||
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<IZUGFeRDAllowanceCharge>(),
|
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(),
|
||||||
Charges = new ArrayList<IZUGFeRDAllowanceCharge>(), LogisticsServiceCharges = new ArrayList<IZUGFeRDAllowanceCharge>();
|
Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>();
|
||||||
protected IZUGFeRDPaymentTerms paymentTerms = null;
|
protected IZUGFeRDPaymentTerms paymentTerms = null;
|
||||||
|
private Date invoiceReferencedIssueDate;
|
||||||
|
private String specifiedProcuringProjectID = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String specifiedProcuringProjectName = null;
|
||||||
|
|
||||||
public Invoice() {
|
public Invoice() {
|
||||||
ZFItems = new ArrayList<IZUGFeRDExportableItem>();
|
ZFItems = new ArrayList<>();
|
||||||
setCurrency("EUR");
|
setCurrency("EUR");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +251,16 @@ public class Invoice implements IExportableTransaction {
|
|||||||
return invoiceReferencedDocumentID;
|
return invoiceReferencedDocumentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getInvoiceReferencedIssueDate() {
|
||||||
|
return invoiceReferencedIssueDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice setInvoiceReferencedIssueDate(Date issueDate) {
|
||||||
|
this.invoiceReferencedIssueDate = issueDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBuyerOrderReferencedDocumentIssueDateTime() {
|
public String getBuyerOrderReferencedDocumentIssueDateTime() {
|
||||||
return buyerOrderReferencedDocumentIssueDateTime;
|
return buyerOrderReferencedDocumentIssueDateTime;
|
||||||
@@ -637,4 +652,23 @@ public class Invoice implements IExportableTransaction {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSpecifiedProcuringProjectID() {
|
||||||
|
return specifiedProcuringProjectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice setSpecifiedProcuringProjectID(String specifiedProcuringProjectID) {
|
||||||
|
this.specifiedProcuringProjectID = specifiedProcuringProjectID;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSpecifiedProcuringProjectName() {
|
||||||
|
return specifiedProcuringProjectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice setSpecifiedProcuringProjectName(String specifiedProcuringProjectName) {
|
||||||
|
this.specifiedProcuringProjectName = specifiedProcuringProjectName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ public interface IExportableTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the ID of the prceding invoice, which is e.g. to be corrected if this is a correction
|
* get the ID of the preceding invoice, which is e.g. to be corrected if this is a correction
|
||||||
*
|
*
|
||||||
* @return the ID of the document
|
* @return the ID of the document
|
||||||
*/
|
*/
|
||||||
@@ -424,6 +424,7 @@ public interface IExportableTransaction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default Date getInvoiceReferencedIssueDate(){return null;}
|
||||||
/**
|
/**
|
||||||
* get the issue timestamp of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
|
* get the issue timestamp of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
|
||||||
*
|
*
|
||||||
@@ -488,4 +489,12 @@ public interface IExportableTransaction {
|
|||||||
default String[] getNotes() {
|
default String[] getNotes() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default String getSpecifiedProcuringProjectName(){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default String getSpecifiedProcuringProjectID(){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
|
import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE;
|
||||||
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
|
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -46,7 +47,6 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
|
|
||||||
protected byte[] zugferdData;
|
protected byte[] zugferdData;
|
||||||
private String paymentTermsDescription;
|
private String paymentTermsDescription;
|
||||||
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd");
|
|
||||||
protected Profile profile = Profiles.getByName("COMFORT", 1);
|
protected Profile profile = Profiles.getByName("COMFORT", 1);
|
||||||
|
|
||||||
|
|
||||||
@@ -169,8 +169,8 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
+ " <ram:ID>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:ID>\n"
|
+ " <ram:ID>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:ID>\n"
|
||||||
+ " <ram:Name>RECHNUNG</ram:Name>\n"
|
+ " <ram:Name>RECHNUNG</ram:Name>\n"
|
||||||
+ " <ram:TypeCode>" + typecode + "</ram:TypeCode>\n"
|
+ " <ram:TypeCode>" + typecode + "</ram:TypeCode>\n"
|
||||||
+ " <ram:IssueDateTime><udt:DateTimeString format=\"102\">"
|
+ " <ram:IssueDateTime>"
|
||||||
+ zugferdDateFormat.format(trans.getIssueDate()) + "</udt:DateTimeString></ram:IssueDateTime>\n" // date
|
+ DATE.udtFormat(trans.getIssueDate()) + "</ram:IssueDateTime>\n" // date
|
||||||
// format
|
// format
|
||||||
// was
|
// was
|
||||||
// 20130605
|
// 20130605
|
||||||
@@ -225,8 +225,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
+ " <ram:OccurrenceDateTime>";
|
+ " <ram:OccurrenceDateTime>";
|
||||||
|
|
||||||
if (trans.getDeliveryDate() != null) {
|
if (trans.getDeliveryDate() != null) {
|
||||||
xml += "<udt:DateTimeString format=\"102\">" + zugferdDateFormat.format(trans.getDeliveryDate())
|
xml += DATE.udtFormat(trans.getDeliveryDate());
|
||||||
+ "</udt:DateTimeString>";
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("No delivery date provided");
|
throw new IllegalStateException("No delivery date provided");
|
||||||
}
|
}
|
||||||
@@ -292,9 +291,9 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasDueDate && (trans.getDueDate() != null)) {
|
if (hasDueDate && (trans.getDueDate() != null)) {
|
||||||
xml = xml + " <ram:DueDateDateTime><udt:DateTimeString format=\"102\">" // $NON-NLS-2$
|
xml = xml + " <ram:DueDateDateTime>" // $NON-NLS-2$
|
||||||
+ zugferdDateFormat.format(trans.getDueDate())
|
+ DATE.udtFormat(trans.getDueDate())
|
||||||
+ "</udt:DateTimeString></ram:DueDateDateTime>\n";// 20130704
|
+ "</ram:DueDateDateTime>\n";// 20130704
|
||||||
|
|
||||||
}
|
}
|
||||||
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n";
|
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n";
|
||||||
@@ -440,8 +439,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
paymentTermsXml += "<ram:Description>" + paymentTerms.getDescription() + "</ram:Description>";
|
paymentTermsXml += "<ram:Description>" + paymentTerms.getDescription() + "</ram:Description>";
|
||||||
if (dueDate != null) {
|
if (dueDate != null) {
|
||||||
paymentTermsXml += "<ram:DueDateDateTime>";
|
paymentTermsXml += "<ram:DueDateDateTime>";
|
||||||
paymentTermsXml += "<udt:DateTimeString format=\"102\">"
|
paymentTermsXml += DATE.udtFormat(dueDate) ;
|
||||||
+ zugferdDateFormat.format(dueDate.getDate()) + "</udt:DateTimeString>";
|
|
||||||
paymentTermsXml += "</ram:DueDateDateTime>";
|
paymentTermsXml += "</ram:DueDateDateTime>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +454,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
|
|||||||
if (discountTerms.getBaseDate() != null) {
|
if (discountTerms.getBaseDate() != null) {
|
||||||
final Date baseDate = discountTerms.getBaseDate();
|
final Date baseDate = discountTerms.getBaseDate();
|
||||||
paymentTermsXml += "<ram:BasisDateTime>";
|
paymentTermsXml += "<ram:BasisDateTime>";
|
||||||
paymentTermsXml += "<udt:DateTimeString format=\"102\">" + zugferdDateFormat.format(baseDate) + "</udt:DateTimeString>";
|
paymentTermsXml += DATE.udtFormat(baseDate) ;
|
||||||
paymentTermsXml += "</ram:BasisDateTime>";
|
paymentTermsXml += "</ram:BasisDateTime>";
|
||||||
|
|
||||||
paymentTermsXml += "<ram:BasisPeriodMeasure unitCode=\"" + discountTerms.getBasePeriodUnitCode() + "\">"
|
paymentTermsXml += "<ram:BasisPeriodMeasure unitCode=\"" + discountTerms.getBasePeriodUnitCode() + "\">"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
|
import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE;
|
||||||
import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE;
|
import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE;
|
||||||
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
|
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
|
||||||
|
|
||||||
@@ -44,8 +45,6 @@ import org.mustangproject.XMLTools;
|
|||||||
|
|
||||||
public class ZUGFeRD2PullProvider implements IXMLProvider {
|
public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||||
|
|
||||||
//// MAIN CLASS
|
|
||||||
protected SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd");
|
|
||||||
protected byte[] zugferdData;
|
protected byte[] zugferdData;
|
||||||
protected IExportableTransaction trans;
|
protected IExportableTransaction trans;
|
||||||
protected TransactionCalculator calc;
|
protected TransactionCalculator calc;
|
||||||
@@ -278,7 +277,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
// xsi:schemaLocation=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100
|
// xsi:schemaLocation=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100
|
||||||
// ../Schema/ZUGFeRD1p0.xsd\""
|
// ../Schema/ZUGFeRD1p0.xsd\""
|
||||||
+ " xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\""
|
+ " xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\""
|
||||||
+ " xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\">\n"
|
+ " xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\""
|
||||||
|
+ " xmlns:qdt=\"urn:un:unece:uncefact:data:standard:QualifiedDataType:100\">\n"
|
||||||
+ " <rsm:ExchangedDocumentContext>\n"
|
+ " <rsm:ExchangedDocumentContext>\n"
|
||||||
// + "
|
// + "
|
||||||
// <ram:TestIndicator><udt:Indicator>"+testBooleanStr+"</udt:Indicator></ram:TestIndicator>\n"
|
// <ram:TestIndicator><udt:Indicator>"+testBooleanStr+"</udt:Indicator></ram:TestIndicator>\n"
|
||||||
@@ -292,8 +292,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
// + " <ram:Name>RECHNUNG</ram:Name>\n"
|
// + " <ram:Name>RECHNUNG</ram:Name>\n"
|
||||||
// + " <ram:TypeCode>380</ram:TypeCode>\n"
|
// + " <ram:TypeCode>380</ram:TypeCode>\n"
|
||||||
+ " <ram:TypeCode>" + typecode + "</ram:TypeCode>\n"
|
+ " <ram:TypeCode>" + typecode + "</ram:TypeCode>\n"
|
||||||
+ " <ram:IssueDateTime><udt:DateTimeString format=\"102\">"
|
+ " <ram:IssueDateTime>"
|
||||||
+ zugferdDateFormat.format(trans.getIssueDate()) + "</udt:DateTimeString></ram:IssueDateTime>\n" // date
|
+ DATE.udtFormat(trans.getIssueDate()) + "</ram:IssueDateTime>\n" // date
|
||||||
+ notes
|
+ notes
|
||||||
+ subjectNote
|
+ subjectNote
|
||||||
+ rebateAgreement
|
+ rebateAgreement
|
||||||
@@ -405,10 +405,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) {
|
if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) {
|
||||||
xml = xml + "<ram:BillingSpecifiedPeriod>";
|
xml = xml + "<ram:BillingSpecifiedPeriod>";
|
||||||
if (currentItem.getDetailedDeliveryPeriodFrom() != null) {
|
if (currentItem.getDetailedDeliveryPeriodFrom() != null) {
|
||||||
xml = xml + "<ram:StartDateTime><udt:DateTimeString format='102'>" + zugferdDateFormat.format(currentItem.getDetailedDeliveryPeriodFrom()) + "</udt:DateTimeString></ram:StartDateTime>";
|
xml = xml + "<ram:StartDateTime>" + DATE.udtFormat(currentItem.getDetailedDeliveryPeriodFrom()) + "</ram:StartDateTime>";
|
||||||
}
|
}
|
||||||
if (currentItem.getDetailedDeliveryPeriodTo() != null) {
|
if (currentItem.getDetailedDeliveryPeriodTo() != null) {
|
||||||
xml = xml + "<ram:EndDateTime><udt:DateTimeString format='102'>" + zugferdDateFormat.format(currentItem.getDetailedDeliveryPeriodTo()) + "</udt:DateTimeString></ram:EndDateTime>";
|
xml = xml + "<ram:EndDateTime>" + DATE.udtFormat(currentItem.getDetailedDeliveryPeriodTo()) + "</ram:EndDateTime>";
|
||||||
}
|
}
|
||||||
xml = xml + "</ram:BillingSpecifiedPeriod>";
|
xml = xml + "</ram:BillingSpecifiedPeriod>";
|
||||||
|
|
||||||
@@ -475,6 +475,15 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trans.getSpecifiedProcuringProjectID() != null) {
|
||||||
|
xml = xml + " <ram:SpecifiedProcuringProject>\n"
|
||||||
|
+ " <ram:ID>"
|
||||||
|
+ XMLTools.encodeXML(trans.getSpecifiedProcuringProjectID()) + "</ram:ID>\n";
|
||||||
|
if(trans.getSpecifiedProcuringProjectName()!= null) {
|
||||||
|
xml += " <ram:Name >" + XMLTools.encodeXML(trans.getSpecifiedProcuringProjectName()) + "</ram:Name>\n";
|
||||||
|
}
|
||||||
|
xml += " </ram:SpecifiedProcuringProject>\n";
|
||||||
|
}
|
||||||
xml = xml + " </ram:ApplicableHeaderTradeAgreement>\n"
|
xml = xml + " </ram:ApplicableHeaderTradeAgreement>\n"
|
||||||
+ " <ram:ApplicableHeaderTradeDelivery>\n";
|
+ " <ram:ApplicableHeaderTradeDelivery>\n";
|
||||||
if (this.trans.getDeliveryAddress() != null) {
|
if (this.trans.getDeliveryAddress() != null) {
|
||||||
@@ -487,8 +496,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
+ " <ram:OccurrenceDateTime>";
|
+ " <ram:OccurrenceDateTime>";
|
||||||
|
|
||||||
if (trans.getDeliveryDate() != null) {
|
if (trans.getDeliveryDate() != null) {
|
||||||
xml += "<udt:DateTimeString format=\"102\">" + zugferdDateFormat.format(trans.getDeliveryDate())
|
xml += DATE.udtFormat(trans.getDeliveryDate());
|
||||||
+ "</udt:DateTimeString>";
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("No delivery date provided");
|
throw new IllegalStateException("No delivery date provided");
|
||||||
}
|
}
|
||||||
@@ -546,10 +554,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
if ((trans.getDetailedDeliveryPeriodFrom() != null) || (trans.getDetailedDeliveryPeriodTo() != null)) {
|
if ((trans.getDetailedDeliveryPeriodFrom() != null) || (trans.getDetailedDeliveryPeriodTo() != null)) {
|
||||||
xml = xml + "<ram:BillingSpecifiedPeriod>";
|
xml = xml + "<ram:BillingSpecifiedPeriod>";
|
||||||
if (trans.getDetailedDeliveryPeriodFrom() != null) {
|
if (trans.getDetailedDeliveryPeriodFrom() != null) {
|
||||||
xml = xml + "<ram:StartDateTime><udt:DateTimeString format='102'>" + zugferdDateFormat.format(trans.getDetailedDeliveryPeriodFrom()) + "</udt:DateTimeString></ram:StartDateTime>";
|
xml = xml + "<ram:StartDateTime>" + DATE.udtFormat(trans.getDetailedDeliveryPeriodFrom()) + "</ram:StartDateTime>";
|
||||||
}
|
}
|
||||||
if (trans.getDetailedDeliveryPeriodTo() != null) {
|
if (trans.getDetailedDeliveryPeriodTo() != null) {
|
||||||
xml = xml + "<ram:EndDateTime><udt:DateTimeString format='102'>" + zugferdDateFormat.format(trans.getDetailedDeliveryPeriodTo()) + "</udt:DateTimeString></ram:EndDateTime>";
|
xml = xml + "<ram:EndDateTime>" + DATE.udtFormat(trans.getDetailedDeliveryPeriodTo()) + "</ram:EndDateTime>";
|
||||||
}
|
}
|
||||||
xml = xml + "</ram:BillingSpecifiedPeriod>";
|
xml = xml + "</ram:BillingSpecifiedPeriod>";
|
||||||
|
|
||||||
@@ -616,9 +624,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasDueDate && (trans.getDueDate() != null)) {
|
if (hasDueDate && (trans.getDueDate() != null)) {
|
||||||
xml = xml + " <ram:DueDateDateTime><udt:DateTimeString format=\"102\">" // $NON-NLS-2$
|
xml = xml + " <ram:DueDateDateTime>" // $NON-NLS-2$
|
||||||
+ zugferdDateFormat.format(trans.getDueDate())
|
+ DATE.udtFormat(trans.getDueDate())
|
||||||
+ "</udt:DateTimeString></ram:DueDateDateTime>\n";// 20130704
|
+ "</ram:DueDateDateTime>\n";// 20130704
|
||||||
|
|
||||||
}
|
}
|
||||||
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n";
|
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n";
|
||||||
@@ -649,8 +657,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
if (trans.getInvoiceReferencedDocumentID() != null) {
|
if (trans.getInvoiceReferencedDocumentID() != null) {
|
||||||
xml = xml + " <ram:InvoiceReferencedDocument>\n"
|
xml = xml + " <ram:InvoiceReferencedDocument>\n"
|
||||||
+ " <ram:IssuerAssignedID>"
|
+ " <ram:IssuerAssignedID>"
|
||||||
+ XMLTools.encodeXML(trans.getInvoiceReferencedDocumentID()) + "</ram:IssuerAssignedID>\n"
|
+ XMLTools.encodeXML(trans.getInvoiceReferencedDocumentID()) + "</ram:IssuerAssignedID>\n";
|
||||||
+ " </ram:InvoiceReferencedDocument>\n";
|
if(trans.getInvoiceReferencedIssueDate()!= null){
|
||||||
|
xml += "<ram:FormattedIssueDateTime>"
|
||||||
|
+ DATE.qdtFormat(trans.getInvoiceReferencedIssueDate())
|
||||||
|
+ "</ram:FormattedIssueDateTime>\n";
|
||||||
|
}
|
||||||
|
xml += " </ram:InvoiceReferencedDocument>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
xml = xml + " </ram:ApplicableHeaderTradeSettlement>\n";
|
xml = xml + " </ram:ApplicableHeaderTradeSettlement>\n";
|
||||||
@@ -698,8 +711,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
paymentTermsXml += "<ram:Description>" + paymentTerms.getDescription() + "</ram:Description>";
|
paymentTermsXml += "<ram:Description>" + paymentTerms.getDescription() + "</ram:Description>";
|
||||||
if (dueDate != null) {
|
if (dueDate != null) {
|
||||||
paymentTermsXml += "<ram:DueDateDateTime>";
|
paymentTermsXml += "<ram:DueDateDateTime>";
|
||||||
paymentTermsXml += "<udt:DateTimeString format=\"102\">"
|
paymentTermsXml += DATE.udtFormat(dueDate) ;
|
||||||
+ zugferdDateFormat.format(dueDate) + "</udt:DateTimeString>";
|
|
||||||
paymentTermsXml += "</ram:DueDateDateTime>";
|
paymentTermsXml += "</ram:DueDateDateTime>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,7 +726,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
if (discountTerms.getBaseDate() != null) {
|
if (discountTerms.getBaseDate() != null) {
|
||||||
final Date baseDate = discountTerms.getBaseDate();
|
final Date baseDate = discountTerms.getBaseDate();
|
||||||
paymentTermsXml += "<ram:BasisDateTime>";
|
paymentTermsXml += "<ram:BasisDateTime>";
|
||||||
paymentTermsXml += "<udt:DateTimeString format=\"102\">" + zugferdDateFormat.format(baseDate) + "</udt:DateTimeString>";
|
paymentTermsXml += DATE.udtFormat(baseDate);
|
||||||
paymentTermsXml += "</ram:BasisDateTime>";
|
paymentTermsXml += "</ram:BasisDateTime>";
|
||||||
|
|
||||||
paymentTermsXml += "<ram:BasisPeriodMeasure unitCode=\"" + discountTerms.getBasePeriodUnitCode() + "\">"
|
paymentTermsXml += "<ram:BasisPeriodMeasure unitCode=\"" + discountTerms.getBasePeriodUnitCode() + "\">"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.mustangproject.ZUGFeRD.model.DateTimeTypeConstants;
|
import org.mustangproject.ZUGFeRD.model.DateTimeTypeConstants;
|
||||||
|
|
||||||
@@ -12,6 +13,8 @@ public enum ZUGFeRDDateFormat {
|
|||||||
|
|
||||||
private String dateTimeType;
|
private String dateTimeType;
|
||||||
private SimpleDateFormat formatter;
|
private SimpleDateFormat formatter;
|
||||||
|
private static final String QDT_FORMAT = "<qdt:DateTimeString format=\"%s\">%s</qdt:DateTimeString>";
|
||||||
|
private static final String UDT_FORMAT = "<udt:DateTimeString format=\"%s\">%s</udt:DateTimeString>";
|
||||||
|
|
||||||
private ZUGFeRDDateFormat(String dateTimeType, SimpleDateFormat formatter) {
|
private ZUGFeRDDateFormat(String dateTimeType, SimpleDateFormat formatter) {
|
||||||
this.dateTimeType = dateTimeType;
|
this.dateTimeType = dateTimeType;
|
||||||
@@ -25,4 +28,14 @@ public enum ZUGFeRDDateFormat {
|
|||||||
public SimpleDateFormat getFormatter() {
|
public SimpleDateFormat getFormatter() {
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String simpleFormat(Date date){
|
||||||
|
return getFormatter().format(date);
|
||||||
|
}
|
||||||
|
public String qdtFormat(Date date){
|
||||||
|
return String.format(QDT_FORMAT, getDateTimeType(), getFormatter().format(date));
|
||||||
|
}
|
||||||
|
public String udtFormat(Date date){
|
||||||
|
return String.format(UDT_FORMAT, getDateTimeType(), getFormatter().format(date));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ public class DocumentCodeTypeConstants {
|
|||||||
public static final String CREDITNOTE = "381";
|
public static final String CREDITNOTE = "381";
|
||||||
public static final String DEBITNOTE = "84";
|
public static final String DEBITNOTE = "84";
|
||||||
public static final String CORRECTEDINVOICE = "384";
|
public static final String CORRECTEDINVOICE = "384";
|
||||||
|
public static final String PARTIAL_BILLING = "326";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,32 +21,32 @@
|
|||||||
*/
|
*/
|
||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
import org.mustangproject.CII.CIIToUBL;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.mustangproject.CII.CIIToUBL;
|
||||||
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
public class UBLTest extends ResourceCase {
|
public class UBLTest extends ResourceCase {
|
||||||
|
|
||||||
public void testUBLBasic() {
|
public void testUBLBasic() {
|
||||||
|
|
||||||
// the writing part
|
// the writing part
|
||||||
CIIToUBL c2u = new CIIToUBL();
|
final CIIToUBL c2u = new CIIToUBL();
|
||||||
String sourceFilename = "factur-x.xml";
|
final String sourceFilename = "factur-x.xml";
|
||||||
File input = getResourceAsFile(sourceFilename);
|
final File input = getResourceAsFile(sourceFilename);
|
||||||
File expectedFile = getResourceAsFile("ubl-conv-ubl-output-factur-x.xml");
|
final File expectedFile = getResourceAsFile("ubl-conv-ubl-output-factur-x.xml");
|
||||||
String expected = null;
|
String expected = null;
|
||||||
String result = null;
|
String result = null;
|
||||||
try {
|
try {
|
||||||
File tempFile = File.createTempFile("ZUGFeRD-UBL-", "-test");
|
final File tempFile = File.createTempFile("ZUGFeRD-UBL-", "-test");
|
||||||
c2u.convert(input, tempFile);
|
c2u.convert(input, tempFile);
|
||||||
expected = ResourceUtilities.readFile(StandardCharsets.UTF_8, expectedFile.getAbsolutePath());
|
expected = ResourceUtilities.readFile(StandardCharsets.UTF_8, expectedFile.getAbsolutePath());
|
||||||
result = ResourceUtilities.readFile(StandardCharsets.UTF_8, tempFile.getAbsolutePath());
|
result = ResourceUtilities.readFile(StandardCharsets.UTF_8, tempFile.getAbsolutePath()).replaceAll("\r\n", "\n");
|
||||||
} catch (IOException e) {
|
} catch (final IOException e) {
|
||||||
fail("Exception should not happen: "+e.getMessage());
|
fail("Exception should not happen: "+e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.mustangproject.validator;
|
package org.mustangproject.validator;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -8,7 +7,6 @@ import java.io.PrintWriter;
|
|||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@@ -16,14 +14,9 @@ import java.util.EnumSet;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.xml.XMLConstants;
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.transform.Source;
|
|
||||||
import javax.xml.transform.stream.StreamSource;
|
|
||||||
import javax.xml.validation.Schema;
|
|
||||||
import javax.xml.validation.SchemaFactory;
|
|
||||||
import javax.xml.xpath.XPath;
|
import javax.xml.xpath.XPath;
|
||||||
import javax.xml.xpath.XPathConstants;
|
import javax.xml.xpath.XPathConstants;
|
||||||
import javax.xml.xpath.XPathExpression;
|
import javax.xml.xpath.XPathExpression;
|
||||||
@@ -75,46 +68,47 @@ public class PDFValidator extends Validator {
|
|||||||
return Arrays.asList(arr).contains(targetValue);
|
return Arrays.asList(arr).contains(targetValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void validate() throws IrrecoverableValidationError {
|
public void validate() throws IrrecoverableValidationError {
|
||||||
|
|
||||||
zfXML = null;
|
zfXML = null;
|
||||||
File file = new File(pdfFilename);
|
final File file = new File(pdfFilename);
|
||||||
// file existence must have been checked before
|
// file existence must have been checked before
|
||||||
BigFileSearcher searcher = new BigFileSearcher();
|
final BigFileSearcher searcher = new BigFileSearcher();
|
||||||
|
|
||||||
byte[] pdfSignature = { '%', 'P', 'D', 'F' };
|
final byte[] pdfSignature = { '%', 'P', 'D', 'F' };
|
||||||
if (searcher.indexOf(file, pdfSignature) != 0) {
|
if (searcher.indexOf(file, pdfSignature) != 0) {
|
||||||
context.addResultItem(
|
context.addResultItem(
|
||||||
new ValidationResultItem(ESeverity.fatal, "Not a PDF file "+pdfFilename).setSection(20).setPart(EPart.pdf));
|
new ValidationResultItem(ESeverity.fatal, "Not a PDF file "+pdfFilename).setSection(20).setPart(EPart.pdf));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long startPDFTime = Calendar.getInstance().getTimeInMillis();
|
final long startPDFTime = Calendar.getInstance().getTimeInMillis();
|
||||||
|
|
||||||
// Step 1 Validate PDF
|
// Step 1 Validate PDF
|
||||||
|
|
||||||
VeraGreenfieldFoundryProvider.initialise();
|
VeraGreenfieldFoundryProvider.initialise();
|
||||||
// Default validator config
|
// Default validator config
|
||||||
ValidatorConfig validatorConfig = ValidatorFactory.defaultConfig();
|
final ValidatorConfig validatorConfig = ValidatorFactory.defaultConfig();
|
||||||
// Default features config
|
// Default features config
|
||||||
FeatureExtractorConfig featureConfig = FeatureFactory.defaultConfig();
|
final FeatureExtractorConfig featureConfig = FeatureFactory.defaultConfig();
|
||||||
// Default plugins config
|
// Default plugins config
|
||||||
PluginsCollectionConfig pluginsConfig = PluginsCollectionConfig.defaultConfig();
|
final PluginsCollectionConfig pluginsConfig = PluginsCollectionConfig.defaultConfig();
|
||||||
// Default fixer config
|
// Default fixer config
|
||||||
MetadataFixerConfig fixerConfig = FixerFactory.defaultConfig();
|
final MetadataFixerConfig fixerConfig = FixerFactory.defaultConfig();
|
||||||
// Tasks configuring
|
// Tasks configuring
|
||||||
EnumSet tasks = EnumSet.noneOf(TaskType.class);
|
final EnumSet tasks = EnumSet.noneOf(TaskType.class);
|
||||||
tasks.add(TaskType.VALIDATE);
|
tasks.add(TaskType.VALIDATE);
|
||||||
// tasks.add(TaskType.EXTRACT_FEATURES);
|
// tasks.add(TaskType.EXTRACT_FEATURES);
|
||||||
// tasks.add(TaskType.FIX_METADATA);
|
// tasks.add(TaskType.FIX_METADATA);
|
||||||
// Creating processor config
|
// Creating processor config
|
||||||
ProcessorConfig processorConfig = ProcessorFactory.fromValues(validatorConfig, featureConfig, pluginsConfig,
|
final ProcessorConfig processorConfig = ProcessorFactory.fromValues(validatorConfig, featureConfig, pluginsConfig,
|
||||||
fixerConfig, tasks);
|
fixerConfig, tasks);
|
||||||
// Creating processor and output stream.
|
// Creating processor and output stream.
|
||||||
ByteArrayOutputStream reportStream = new ByteArrayOutputStream();
|
final ByteArrayOutputStream reportStream = new ByteArrayOutputStream();
|
||||||
try (BatchProcessor processor = ProcessorFactory.fileBatchProcessor(processorConfig)) {
|
try (BatchProcessor processor = ProcessorFactory.fileBatchProcessor(processorConfig)) {
|
||||||
// Generating list of files for processing
|
// Generating list of files for processing
|
||||||
List<File> files = new ArrayList<>();
|
final List<File> files = new ArrayList<>();
|
||||||
files.add(new File(pdfFilename));
|
files.add(new File(pdfFilename));
|
||||||
// starting the processor
|
// starting the processor
|
||||||
processor.process(files, ProcessorFactory.getHandler(FormatOption.MRR, true, reportStream, 100,
|
processor.process(files, ProcessorFactory.getHandler(FormatOption.MRR, true, reportStream, 100,
|
||||||
@@ -122,25 +116,25 @@ public class PDFValidator extends Validator {
|
|||||||
|
|
||||||
pdfReport = reportStream.toString("utf-8").replaceAll("<\\?xml version=\"1\\.0\" encoding=\"utf-8\"\\?>",
|
pdfReport = reportStream.toString("utf-8").replaceAll("<\\?xml version=\"1\\.0\" encoding=\"utf-8\"\\?>",
|
||||||
"");
|
"");
|
||||||
} catch (VeraPDFException e) {
|
} catch (final VeraPDFException e) {
|
||||||
ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(6)
|
final ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(6)
|
||||||
.setPart(EPart.pdf);
|
.setPart(EPart.pdf);
|
||||||
StringWriter sw = new StringWriter();
|
final StringWriter sw = new StringWriter();
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
final PrintWriter pw = new PrintWriter(sw);
|
||||||
e.printStackTrace(pw);
|
e.printStackTrace(pw);
|
||||||
vri.setStacktrace(sw.toString());
|
vri.setStacktrace(sw.toString());
|
||||||
context.addResultItem(vri);
|
context.addResultItem(vri);
|
||||||
} catch (IOException excep) {
|
} catch (final IOException excep) {
|
||||||
context.addResultItem(new ValidationResultItem(ESeverity.exception, excep.getMessage()).setSection(7)
|
context.addResultItem(new ValidationResultItem(ESeverity.exception, excep.getMessage()).setSection(7)
|
||||||
.setPart(EPart.pdf).setStacktrace(excep.getStackTrace().toString()));
|
.setPart(EPart.pdf).setStacktrace(excep.getStackTrace().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 2 validate XMP
|
// step 2 validate XMP
|
||||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(pdfFilename);
|
final ZUGFeRDImporter zi = new ZUGFeRDImporter(pdfFilename);
|
||||||
String xmp = zi.getXMP();
|
final String xmp = zi.getXMP();
|
||||||
|
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
Document docXMP;
|
final Document docXMP;
|
||||||
|
|
||||||
if (xmp.length() == 0) {
|
if (xmp.length() == 0) {
|
||||||
context.addResultItem(new ValidationResultItem(ESeverity.error, "Invalid XMP Metadata not found")
|
context.addResultItem(new ValidationResultItem(ESeverity.error, "Invalid XMP Metadata not found")
|
||||||
@@ -153,15 +147,15 @@ public class PDFValidator extends Validator {
|
|||||||
* <zf:Version>1.0</zf:Version>
|
* <zf:Version>1.0</zf:Version>
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
final DocumentBuilder builder = factory.newDocumentBuilder();
|
||||||
InputSource is = new InputSource(new StringReader(xmp));
|
final InputSource is = new InputSource(new StringReader(xmp));
|
||||||
docXMP = builder.parse(is);
|
docXMP = builder.parse(is);
|
||||||
|
|
||||||
XPathFactory xpathFactory = XPathFactory.newInstance();
|
final XPathFactory xpathFactory = XPathFactory.newInstance();
|
||||||
|
|
||||||
// Create XPath object XPath xpath = xpathFactory.newXPath(); XPathExpression
|
// Create XPath object XPath xpath = xpathFactory.newXPath(); XPathExpression
|
||||||
|
|
||||||
XPath xpath = xpathFactory.newXPath();
|
final XPath xpath = xpathFactory.newXPath();
|
||||||
// xpath.compile("//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/[local-name()=\"ID\"]");
|
// xpath.compile("//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/[local-name()=\"ID\"]");
|
||||||
// evaluate expression result on XML document ndList = (NodeList)
|
// evaluate expression result on XML document ndList = (NodeList)
|
||||||
|
|
||||||
@@ -179,7 +173,7 @@ public class PDFValidator extends Validator {
|
|||||||
boolean conformanceLevelValid=false;
|
boolean conformanceLevelValid=false;
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
|
|
||||||
String[] valueArray = { "BASIC WL", "BASIC", "MINIMUM", "EN 16931", "COMFORT", "CIUS", "EXTENDED", "XRECHNUNG" };
|
final String[] valueArray = { "BASIC WL", "BASIC", "MINIMUM", "EN 16931", "COMFORT", "CIUS", "EXTENDED", "XRECHNUNG" };
|
||||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||||
conformanceLevelValid=true;
|
conformanceLevelValid=true;
|
||||||
}
|
}
|
||||||
@@ -220,7 +214,7 @@ public class PDFValidator extends Validator {
|
|||||||
}
|
}
|
||||||
boolean documentFilenameValid=false;
|
boolean documentFilenameValid=false;
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
String[] valueArray = { "factur-x.xml", "ZUGFeRD-invoice.xml", "zugferd-invoice.xml", "xrechnung.xml" , "order-x.xml" };
|
final String[] valueArray = { "factur-x.xml", "ZUGFeRD-invoice.xml", "zugferd-invoice.xml", "xrechnung.xml" , "order-x.xml" };
|
||||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||||
documentFilenameValid=true;
|
documentFilenameValid=true;
|
||||||
}
|
}
|
||||||
@@ -246,7 +240,7 @@ public class PDFValidator extends Validator {
|
|||||||
|
|
||||||
boolean versionValid=false;
|
boolean versionValid=false;
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
String[] valueArray = { "1.0", "2p0", "1.2", "2.0" }; //1.2 and 2.0 are for xrechnung 1.2, 2p0 can be ZF 2.0, 2.1, 2.1.1
|
final String[] valueArray = { "1.0", "2p0", "1.2", "2.0" }; //1.2 and 2.0 are for xrechnung 1.2, 2p0 can be ZF 2.0, 2.1, 2.1.1
|
||||||
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
|
||||||
versionValid=true;
|
versionValid=true;
|
||||||
} // e.g. 1.0
|
} // e.g. 1.0
|
||||||
@@ -258,26 +252,26 @@ public class PDFValidator extends Validator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SAXException e) {
|
} catch (final SAXException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
} catch (IOException e) {
|
} catch (final IOException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
} catch (ParserConfigurationException e) {
|
} catch (final ParserConfigurationException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
} catch (XPathExpressionException e) {
|
} catch (final XPathExpressionException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
zfXML = zi.getUTF8();
|
zfXML = zi.getUTF8();
|
||||||
|
|
||||||
// step 3 find signatures
|
// step 3 find signatures
|
||||||
try {
|
try {
|
||||||
byte[] symtraxSignature = "Symtrax".getBytes("UTF-8");
|
final byte[] symtraxSignature = "Symtrax".getBytes("UTF-8");
|
||||||
byte[] mustangSignature = "via mustangproject".getBytes("UTF-8");
|
final byte[] mustangSignature = "via mustangproject".getBytes("UTF-8");
|
||||||
byte[] facturxpythonSignature = "by Alexis de Lattre".getBytes("UTF-8");
|
final byte[] facturxpythonSignature = "by Alexis de Lattre".getBytes("UTF-8");
|
||||||
byte[] intarsysSignature = "intarsys ".getBytes("UTF-8");
|
final byte[] intarsysSignature = "intarsys ".getBytes("UTF-8");
|
||||||
byte[] konikSignature = "Konik".getBytes("UTF-8");
|
final byte[] konikSignature = "Konik".getBytes("UTF-8");
|
||||||
byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes("UTF-8");
|
final byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes("UTF-8");
|
||||||
byte[] ghostscriptSignature = "%%Invocation:".getBytes("UTF-8");
|
final byte[] ghostscriptSignature = "%%Invocation:".getBytes("UTF-8");
|
||||||
|
|
||||||
if (searcher.indexOf(file, symtraxSignature) != -1) {
|
if (searcher.indexOf(file, symtraxSignature) != -1) {
|
||||||
Signature = "Symtrax";
|
Signature = "Symtrax";
|
||||||
@@ -297,13 +291,13 @@ public class PDFValidator extends Validator {
|
|||||||
|
|
||||||
context.setSignature(Signature);
|
context.setSignature(Signature);
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (final UnsupportedEncodingException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 4:validate additional data
|
// step 4:validate additional data
|
||||||
HashMap<String, byte[]> additionalData=zi.getAdditionalData();
|
final HashMap<String, byte[]> additionalData=zi.getAdditionalData();
|
||||||
for (String filename : additionalData.keySet()) {
|
for (final String filename : additionalData.keySet()) {
|
||||||
// validating xml in byte[] additionalData.get(filename)
|
// validating xml in byte[] additionalData.get(filename)
|
||||||
LOGGER.info("validating additionalData " + filename);
|
LOGGER.info("validating additionalData " + filename);
|
||||||
validateSchema(additionalData.get(filename), "ad/basic/additional_data_base_schema.xsd", 2, EPart.pdf);
|
validateSchema(additionalData.get(filename), "ad/basic/additional_data_base_schema.xsd", 2, EPart.pdf);
|
||||||
@@ -312,7 +306,7 @@ public class PDFValidator extends Validator {
|
|||||||
|
|
||||||
//end
|
//end
|
||||||
|
|
||||||
long endTime = Calendar.getInstance().getTimeInMillis();
|
final long endTime = Calendar.getInstance().getTimeInMillis();
|
||||||
if (!pdfReport.contains("validationReports compliant=\"1\"")) {
|
if (!pdfReport.contains("validationReports compliant=\"1\"")) {
|
||||||
context.setInvalid();
|
context.setInvalid();
|
||||||
}
|
}
|
||||||
@@ -323,7 +317,7 @@ public class PDFValidator extends Validator {
|
|||||||
}
|
}
|
||||||
context.addCustomXML(pdfReport + "<info><signature>"
|
context.addCustomXML(pdfReport + "<info><signature>"
|
||||||
+ ((context.getSignature() != null) ? context.getSignature() : "unknown")
|
+ ((context.getSignature() != null) ? context.getSignature() : "unknown")
|
||||||
+ "</signature><duration unit='ms'>" + (endTime - startPDFTime) + "</duration></info>");
|
+ "</signature><duration unit=\"ms\">" + (endTime - startPDFTime) + "</duration></info>");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class ValidationContext {
|
|||||||
|
|
||||||
public ValidationContext(Logger log) {
|
public ValidationContext(Logger log) {
|
||||||
logger = log;
|
logger = log;
|
||||||
results = new Vector<ValidationResultItem>();
|
results = new Vector<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResultItem(ValidationResultItem vr) throws IrrecoverableValidationError {
|
public void addResultItem(ValidationResultItem vr) throws IrrecoverableValidationError {
|
||||||
@@ -110,14 +110,14 @@ public class ValidationContext {
|
|||||||
res += "<messages>";
|
res += "<messages>";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ValidationResultItem validationResultItem : results) {
|
for (final ValidationResultItem validationResultItem : results) {
|
||||||
// xml and pdf are handled in their respective sections
|
// xml and pdf are handled in their respective sections
|
||||||
res += validationResultItem.getXMLOnce() + "\n";
|
res += validationResultItem.getXMLOnce() + "\n";
|
||||||
}
|
}
|
||||||
if (results.size() > 0) {
|
if (results.size() > 0) {
|
||||||
res += "</messages>";
|
res += "</messages>";
|
||||||
}
|
}
|
||||||
res += "<summary status='" + (isValid ? "valid" : "invalid") + "'/>";
|
res += "<summary status=\"" + (isValid ? "valid" : "invalid") + "\"/>";
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +126,9 @@ public class ValidationContext {
|
|||||||
* @return the unique error types as comma separated string
|
* @return the unique error types as comma separated string
|
||||||
*/
|
*/
|
||||||
public String getCSVResult() {
|
public String getCSVResult() {
|
||||||
ArrayList<String> errorcodes = new ArrayList<String>();
|
final ArrayList<String> errorcodes = new ArrayList<>();
|
||||||
for (ValidationResultItem validationResultItem : results) {
|
for (final ValidationResultItem validationResultItem : results) {
|
||||||
String errorCodeStr=Integer.toString(validationResultItem.getSection());
|
final String errorCodeStr=Integer.toString(validationResultItem.getSection());
|
||||||
errorcodes.add(errorCodeStr);
|
errorcodes.add(errorCodeStr);
|
||||||
}
|
}
|
||||||
return String.join(",", errorcodes);
|
return String.join(",", errorcodes);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package org.mustangproject.validator;
|
package org.mustangproject.validator;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -22,13 +25,14 @@ import org.w3c.dom.Document;
|
|||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import com.helger.schematron.ISchematronResource;
|
||||||
|
import com.helger.schematron.svrl.SVRLHelper;
|
||||||
import com.helger.schematron.svrl.jaxb.FailedAssert;
|
import com.helger.schematron.svrl.jaxb.FailedAssert;
|
||||||
import com.helger.schematron.svrl.jaxb.FiredRule;
|
import com.helger.schematron.svrl.jaxb.FiredRule;
|
||||||
import com.helger.schematron.svrl.jaxb.SchematronOutputType;
|
import com.helger.schematron.svrl.jaxb.SchematronOutputType;
|
||||||
import com.helger.schematron.ISchematronResource;
|
|
||||||
import com.helger.schematron.xslt.SchematronResourceXSLT;
|
import com.helger.schematron.xslt.SchematronResourceXSLT;
|
||||||
import com.helger.schematron.svrl.SVRLHelper;
|
|
||||||
import org.xml.sax.InputSource;
|
|
||||||
|
|
||||||
public class XMLValidator extends Validator {
|
public class XMLValidator extends Validator {
|
||||||
|
|
||||||
@@ -54,18 +58,19 @@ public class XMLValidator extends Validator {
|
|||||||
* @param name the absolute filename of an xml file to validate
|
* @param name the absolute filename of an xml file to validate
|
||||||
* @throws IrrecoverableValidationError
|
* @throws IrrecoverableValidationError
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setFilename(String name) throws IrrecoverableValidationError { // from XML Filename
|
public void setFilename(String name) throws IrrecoverableValidationError { // from XML Filename
|
||||||
filename = name;
|
filename = name;
|
||||||
// file existence must have been checked before
|
// file existence must have been checked before
|
||||||
|
|
||||||
try {
|
try {
|
||||||
zfXML = new String(XMLTools.removeBOM(Files.readAllBytes(Paths.get(name))), StandardCharsets.UTF_8);
|
zfXML = new String(XMLTools.removeBOM(Files.readAllBytes(Paths.get(name))), StandardCharsets.UTF_8);
|
||||||
} catch (IOException e) {
|
} catch (final IOException e) {
|
||||||
|
|
||||||
ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(9)
|
final ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(9)
|
||||||
.setPart(EPart.fx);
|
.setPart(EPart.fx);
|
||||||
StringWriter sw = new StringWriter();
|
final StringWriter sw = new StringWriter();
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
final PrintWriter pw = new PrintWriter(sw);
|
||||||
e.printStackTrace(pw);
|
e.printStackTrace(pw);
|
||||||
vri.setStacktrace(sw.toString());
|
vri.setStacktrace(sw.toString());
|
||||||
context.addResultItem(vri);
|
context.addResultItem(vri);
|
||||||
@@ -105,13 +110,13 @@ public class XMLValidator extends Validator {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void validate() throws IrrecoverableValidationError {
|
public void validate() throws IrrecoverableValidationError {
|
||||||
long startXMLTime = Calendar.getInstance().getTimeInMillis();
|
final long startXMLTime = Calendar.getInstance().getTimeInMillis();
|
||||||
firedRules = 0;
|
firedRules = 0;
|
||||||
failedRules = 0;
|
failedRules = 0;
|
||||||
|
|
||||||
|
|
||||||
if (zfXML.isEmpty()) {
|
if (zfXML.isEmpty()) {
|
||||||
ValidationResultItem res = new ValidationResultItem(ESeverity.exception,
|
final ValidationResultItem res = new ValidationResultItem(ESeverity.exception,
|
||||||
"XML data not found in " + filename
|
"XML data not found in " + filename
|
||||||
+ ": did you specify a pdf or xml file and does the xml file contain an embedded XML file?")
|
+ ": did you specify a pdf or xml file and does the xml file contain an embedded XML file?")
|
||||||
.setSection(3);
|
.setSection(3);
|
||||||
@@ -136,33 +141,33 @@ public class XMLValidator extends Validator {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
dbf.setNamespaceAware(true); // otherwise we can not act namespace independently, i.e. use
|
dbf.setNamespaceAware(true); // otherwise we can not act namespace independently, i.e. use
|
||||||
// document.getElementsByTagNameNS("*",...
|
// document.getElementsByTagNameNS("*",...
|
||||||
|
|
||||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
final DocumentBuilder db = dbf.newDocumentBuilder();
|
||||||
InputSource is = new InputSource(new StringReader(zfXML));
|
final InputSource is = new InputSource(new StringReader(zfXML));
|
||||||
Document doc = db.parse(is);
|
final Document doc = db.parse(is);
|
||||||
|
|
||||||
Element root = doc.getDocumentElement();
|
final Element root = doc.getDocumentElement();
|
||||||
|
|
||||||
NodeList ndList;
|
final NodeList ndList;
|
||||||
|
|
||||||
// rootNode = document.getDocumentElement();
|
// rootNode = document.getDocumentElement();
|
||||||
// ApplicableSupplyChainTradeSettlement
|
// ApplicableSupplyChainTradeSettlement
|
||||||
|
|
||||||
// Create XPathFactory object
|
// Create XPathFactory object
|
||||||
XPathFactory xpathFactory = XPathFactory.newInstance();
|
final XPathFactory xpathFactory = XPathFactory.newInstance();
|
||||||
|
|
||||||
// Create XPath object
|
// Create XPath object
|
||||||
XPath xpath = xpathFactory.newXPath();
|
final XPath xpath = xpathFactory.newXPath();
|
||||||
XPathExpression expr = xpath.compile(
|
final XPathExpression expr = xpath.compile(
|
||||||
"//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/*[local-name()=\"ID\"]/text()");
|
"//*[local-name()=\"GuidelineSpecifiedDocumentContextParameter\"]/*[local-name()=\"ID\"]/text()");
|
||||||
// evaluate expression result on XML document
|
// evaluate expression result on XML document
|
||||||
ndList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
|
ndList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
|
||||||
|
|
||||||
for (int bookingIndex = 0; bookingIndex < ndList.getLength(); bookingIndex++) {
|
for (int bookingIndex = 0; bookingIndex < ndList.getLength(); bookingIndex++) {
|
||||||
Node booking = ndList.item(bookingIndex);
|
final Node booking = ndList.item(bookingIndex);
|
||||||
// if there is a attribute in the tag number:value
|
// if there is a attribute in the tag number:value
|
||||||
// urn:ferd:CrossIndustryDocument:invoice:1p0:extended
|
// urn:ferd:CrossIndustryDocument:invoice:1p0:extended
|
||||||
// setForeignReference(booking.getTextContent());
|
// setForeignReference(booking.getTextContent());
|
||||||
@@ -324,24 +329,24 @@ public class XMLValidator extends Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (IrrecoverableValidationError er) {
|
} catch (final IrrecoverableValidationError er) {
|
||||||
throw er;
|
throw er;
|
||||||
} catch (Exception e) {
|
} catch (final Exception e) {
|
||||||
ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(22)
|
final ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(22)
|
||||||
.setPart(EPart.fx);
|
.setPart(EPart.fx);
|
||||||
StringWriter sw = new StringWriter();
|
final StringWriter sw = new StringWriter();
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
final PrintWriter pw = new PrintWriter(sw);
|
||||||
e.printStackTrace(pw);
|
e.printStackTrace(pw);
|
||||||
vri.setStacktrace(sw.toString());
|
vri.setStacktrace(sw.toString());
|
||||||
context.addResultItem(vri);
|
context.addResultItem(vri);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
long endTime = Calendar.getInstance().getTimeInMillis();
|
final long endTime = Calendar.getInstance().getTimeInMillis();
|
||||||
|
|
||||||
context.addCustomXML("<info><version>" + ((context.getGeneration() != null) ? context.getGeneration() : "invalid")
|
context.addCustomXML("<info><version>" + ((context.getGeneration() != null) ? context.getGeneration() : "invalid")
|
||||||
+ "</version><profile>" + ((context.getProfile() != null) ? context.getProfile() : "invalid") +
|
+ "</version><profile>" + ((context.getProfile() != null) ? context.getProfile() : "invalid") +
|
||||||
"</profile><validator version=\"" + XMLValidator.class.getPackage().getImplementationVersion() + "\"></validator><rules><fired>" + firedRules + "</fired><failed>" + failedRules + "</failed></rules>" + "<duration unit='ms'>" + (endTime - startXMLTime) + "</duration></info>");
|
"</profile><validator version=\"" + XMLValidator.class.getPackage().getImplementationVersion() + "\"></validator><rules><fired>" + firedRules + "</fired><failed>" + failedRules + "</failed></rules>" + "<duration unit=\"ms\">" + (endTime - startXMLTime) + "</duration></info>");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,20 +382,20 @@ public class XMLValidator extends Validator {
|
|||||||
throw new IllegalArgumentException(xsltFilename + " is invalid Schematron!");
|
throw new IllegalArgumentException(xsltFilename + " is invalid Schematron!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SchematronOutputType sout;
|
final SchematronOutputType sout;
|
||||||
try {
|
try {
|
||||||
sout = aResSCH
|
sout = aResSCH
|
||||||
.applySchematronValidationToSVRL(new StreamSource(new StringReader(xml)));
|
.applySchematronValidationToSVRL(new StreamSource(new StringReader(xml)));
|
||||||
} catch (Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new IrrecoverableValidationError(e.getMessage());
|
throw new IrrecoverableValidationError(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Object> failedAsserts = sout.getActivePatternAndFiredRuleAndFailedAssert();
|
final List<Object> failedAsserts = sout.getActivePatternAndFiredRuleAndFailedAssert();
|
||||||
if (failedAsserts.size() > 0) {
|
if (failedAsserts.size() > 0) {
|
||||||
for (Object object : failedAsserts) {
|
for (final Object object : failedAsserts) {
|
||||||
if (object instanceof FailedAssert) {
|
if (object instanceof FailedAssert) {
|
||||||
|
|
||||||
FailedAssert failedAssert = (FailedAssert) object;
|
final FailedAssert failedAssert = (FailedAssert) object;
|
||||||
LOGGER.info("FailedAssert ", failedAssert);
|
LOGGER.info("FailedAssert ", failedAssert);
|
||||||
|
|
||||||
context.addResultItem(new ValidationResultItem(severity, SVRLHelper.getAsString(failedAssert.getText()))
|
context.addResultItem(new ValidationResultItem(severity, SVRLHelper.getAsString(failedAssert.getText()))
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDValidator.class.getCanonicalName()); // log
|
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDValidator.class.getCanonicalName()); // log
|
||||||
|
|
||||||
public void testPDFValidation() {
|
public void testPDFValidation() {
|
||||||
ValidationContext vc = new ValidationContext(null);
|
final ValidationContext vc = new ValidationContext(null);
|
||||||
PDFValidator pv = new PDFValidator(vc);
|
final PDFValidator pv = new PDFValidator(vc);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
pv.setFilename(tempFile.getAbsolutePath());
|
pv.setFilename(tempFile.getAbsolutePath());
|
||||||
pv.validate();
|
pv.validate();
|
||||||
String actual = pv.getXMLResult();
|
String actual = pv.getXMLResult();
|
||||||
assertEquals(true, actual.contains("summary status='valid"));
|
assertEquals(true, actual.contains("summary status=\"valid"));
|
||||||
assertEquals(false, actual.contains("summary status='invalid"));
|
assertEquals(false, actual.contains("summary status=\"invalid"));
|
||||||
|
|
||||||
XMLValidator xv = new XMLValidator(vc);
|
final XMLValidator xv = new XMLValidator(vc);
|
||||||
xv.setStringContent(pv.getRawXML());
|
xv.setStringContent(pv.getRawXML());
|
||||||
xv.validate();
|
xv.validate();
|
||||||
actual = vc.getXMLResult();
|
actual = vc.getXMLResult();
|
||||||
@@ -58,16 +58,16 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
actual.contains("validationReports compliant=\"1\" nonCompliant=\"0\" failedJobs=\"0\">"));
|
actual.contains("validationReports compliant=\"1\" nonCompliant=\"0\" failedJobs=\"0\">"));
|
||||||
|
|
||||||
assertEquals(false, actual.contains("<error"));
|
assertEquals(false, actual.contains("<error"));
|
||||||
} catch (IrrecoverableValidationError e) {
|
} catch (final IrrecoverableValidationError e) {
|
||||||
// ignore, will be in XML output anyway
|
// ignore, will be in XML output anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPDFXMLValidation() {
|
public void testPDFXMLValidation() {
|
||||||
ValidationContext vc = new ValidationContext(null);
|
final ValidationContext vc = new ValidationContext(null);
|
||||||
try {
|
try {
|
||||||
PDFValidator pv = new PDFValidator(vc);
|
final PDFValidator pv = new PDFValidator(vc);
|
||||||
|
|
||||||
File tempFile = getResourceAsFile("attributeBasedXMP_zugferd_2p0_EN16931_Einfach.pdf");// need a more
|
File tempFile = getResourceAsFile("attributeBasedXMP_zugferd_2p0_EN16931_Einfach.pdf");// need a more
|
||||||
// invalid file here
|
// invalid file here
|
||||||
@@ -99,7 +99,7 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
xmlvres = xv.getXMLResult();
|
xmlvres = xv.getXMLResult();
|
||||||
assertEquals(true, pdfvres.contains("valid") && !pdfvres.contains("invalid"));
|
assertEquals(true, pdfvres.contains("valid") && !pdfvres.contains("invalid"));
|
||||||
assertEquals(true, xmlvres.contains("valid") && !xmlvres.contains("invalid"));
|
assertEquals(true, xmlvres.contains("valid") && !xmlvres.contains("invalid"));
|
||||||
} catch (IrrecoverableValidationError e) {
|
} catch (final IrrecoverableValidationError e) {
|
||||||
// ignore, will be in XML output anyway
|
// ignore, will be in XML output anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
|
|
||||||
public void testXMPValidation() {
|
public void testXMPValidation() {
|
||||||
|
|
||||||
ValidationContext vc = new ValidationContext(null);
|
final ValidationContext vc = new ValidationContext(null);
|
||||||
PDFValidator pv = new PDFValidator(vc);
|
final PDFValidator pv = new PDFValidator(vc);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
File tempFile = getResourceAsFile("invalidXMP.pdf");
|
File tempFile = getResourceAsFile("invalidXMP.pdf");
|
||||||
@@ -130,7 +130,7 @@ public class PDFValidatorTest extends ResourceCase {
|
|||||||
|
|
||||||
assertEquals(false, actual.contains("<error"));// issue 18: "ConformanceLevel not found" should not be
|
assertEquals(false, actual.contains("<error"));// issue 18: "ConformanceLevel not found" should not be
|
||||||
// reported since it's actually there
|
// reported since it's actually there
|
||||||
} catch (IrrecoverableValidationError e) {
|
} catch (final IrrecoverableValidationError e) {
|
||||||
// ignore, will be in XML output anyway
|
// ignore, will be in XML output anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user