Removed server. Moved libraryBasic to library and libraryExtended to validator to prevent confusion with profiles.

This commit is contained in:
Jochen Stärk
2020-05-24 10:45:04 +02:00
parent 40c9065b3d
commit fed2d08dc9
224 changed files with 333 additions and 2502 deletions

View File

@@ -0,0 +1,43 @@
package org.mustangproject;
public class XMLTools {
public static String encodeXML(CharSequence s) {
StringBuilder sb = new StringBuilder();
int len = s.length();
for (int i=0;i<len;i++) {
int c = s.charAt(i);
if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode
}
if (c < 0x80) { // ASCII range: test most common case first
if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
switch(c) {
case '&': sb.append("&amp;"); break;
case '>': sb.append("&gt;"); break;
case '<': sb.append("&lt;"); break;
// Uncomment next two if encoding for an XML attribute
// case '\'' sb.append("&apos;"); break;
// case '\"' sb.append("&quot;"); break;
// Uncomment next three if you prefer, but not required
// case '\n' sb.append("&#10;"); break;
// case '\r' sb.append("&#13;"); break;
// case '\t' sb.append("&#9;"); break;
default: sb.append((char)c);
}
}
} else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
sb.append("&#x");
sb.append(Integer.toHexString(c));
sb.append(';');
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,85 @@
package org.mustangproject.ZUGFeRD;
public class Contact implements IZUGFeRDExportableContact {
protected String name,phone,email,zip,street,location,country;
public Contact(String name, String phone, String email, String street, String zip, String location, String country) {
this.name = name;
this.phone = phone;
this.email = email;
this.street = street;
this.zip = zip;
this.location = location;
this.country = country;
}
@Override
public String getName() {
return name;
}
public Contact setName(String name) {
this.name = name;
return this;
}
@Override
public String getPhone() {
return phone;
}
public Contact setPhone(String phone) {
this.phone = phone;
return this;
}
public String getEMail() {
return email;
}
public Contact setEMail(String email) {
this.email = email;
return this;
}
public String getZIP() {
return zip;
}
public Contact setZIP(String zip) {
this.zip = zip;
return this;
}
@Override
public String getStreet() {
return street;
}
public Contact setStreet(String street) {
this.street = street;
return this;
}
@Override
public String getLocation() {
return location;
}
public Contact setLocation(String location) {
this.location = location;
return this;
}
@Override
public String getCountry() {
return country;
}
public Contact setCountry(String country) {
this.country = country;
return this;
}
}

View File

@@ -0,0 +1,61 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public class CustomXMLProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
@Override
public byte[] getXML() {
return zugferdData;
}
public void setXML(byte[] newData) {
String zf = new String(newData);
if (!zf.contains("CrossIndustry")) {
throw new RuntimeException("ZUGFeRD XML does not contain (<rsm:)CrossIndustry and can thus not be valid");
}
zugferdData = newData;
}
@Override
public void generateXML(IZUGFeRDExportableTransaction trans) {
// TODO Auto-generated method stub
}
@Override
public void setTest() {
// TODO Auto-generated method stub
}
@Override
public String getProfile() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setProfile(ZUGFeRDConformanceLevel level) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,66 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.io.IOException;
import java.io.InputStream;
public interface IExporterFactory {
/**
* factory: loads a PDF file and returns an appropriate exporter
*
* @param pdfFilename binary of a PDF/A1 compliant document
* @return the generated exporter
* @throws IOException if anything is wrong with filename
*/
public ZUGFeRDExporter load(String pdfFilename) throws IOException;
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfBinary binary of a PDF/A1 compliant document
* @return the generated exporter
* @throws IOException (should not happen at all)
*/
public ZUGFeRDExporter load(byte[] pdfBinary) throws IOException;
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfSource source to read a PDF/A1 compliant document from
* @throws IOException if anything is wrong with inputstream
* @return the generated ZUGFeRDExporter
*/
public ZUGFeRDExporter load(InputStream pdfSource) throws IOException;
public IExporterFactory setCreator(String creator);
public IExporterFactory setConformanceLevel(PDFAConformanceLevel newLevel);
public IExporterFactory setProducer(String producer);
public IExporterFactory setZUGFeRDVersion(int version);
public IExporterFactory ignorePDFAErrors();
public IExporterFactory setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel zugferdConformanceLevel);
}

View File

@@ -0,0 +1,27 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public interface IProfileProvider {
public String getProfile();
public void setProfile(ZUGFeRDConformanceLevel level);
}

View File

@@ -0,0 +1,29 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public interface IXMLProvider {
public byte[] getXML();
public void setTest();
public void generateXML(IZUGFeRDExportableTransaction trans);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2015 AlexanderSchmidt.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
import java.math.BigDecimal;
/**
* @author AlexanderSchmidt
*/
public interface IZUGFeRDAllowanceCharge {
BigDecimal getTotalAmount();
String getReason();
BigDecimal getTaxPercent();
default String getCategoryCode() {
return TaxCategoryCodeTypeConstants.STANDARDRATE;
}
}

View File

@@ -0,0 +1,13 @@
package org.mustangproject.ZUGFeRD;
import java.util.Date;
public interface IZUGFeRDDate {
Date getDate();
default ZUGFeRDDateFormat getFormat() {
return ZUGFeRDDateFormat.DATE;
}
}

View File

@@ -0,0 +1,136 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation neccessary interface for ZUGFeRD exporter Licensed under the APLv2
*
* @author jstaerk
* @version 1.2.0
* dated 2014-05-10
*/
public interface IZUGFeRDExportableContact {
/**
* customer identification assigned by the seller
*
* @return customer identification
*/
default String getID() {
return null;
}
/**
* customer global identification assigned by the seller
*
* @return customer identification
*/
default String getGlobalID() {
return null;
}
/**
* customer global identification scheme
*
* @return customer identification
*/
default String getGlobalIDScheme() {
return null;
}
/**
* First and last name of the recipient
*
* @return First and last name of the recipient
*/
default String getName() {
return null;
}
default String getPhone() {
return null;
}
default String getEMail() {
return null;
}
/**
* Postal code of the recipient
*
* @return Postal code of the recipient
*/
default String getZIP() {
return null;
}
/**
* VAT ID (Umsatzsteueridentifikationsnummer) of the contact
*
* @return VAT ID (Umsatzsteueridentifikationsnummer) of the contact
*/
default String getVATID() {
return null;
}
/**
* two-letter country code of the contact
*
* @return two-letter iso country code of the contact
*/
default String getCountry() {
return null;
}
/**
* Returns the city of the contact
*
* @return Returns the city of the recipient
*/
default String getLocation() {
return null;
}
/**
* Returns the street address (street+number) of the contact
*
* @return street address (street+number) of the contact
*/
default String getStreet() {
return null;
}
/**
* returns additional address information which is display in xml tag "LineTwo"
*
* @return additional address information
*/
default String getAdditionalAddress() {
return null;
}
}

View File

@@ -0,0 +1,64 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation
* Neccessary interface for ZUGFeRD exporter
* Licensed under the APLv2
* @date 2014-05-10
* @version 1.2.0
* @author jstaerk
* */
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
import java.math.BigDecimal;
public interface IZUGFeRDExportableItem {
IZUGFeRDExportableProduct getProduct();
IZUGFeRDAllowanceCharge[] getItemAllowances();
IZUGFeRDAllowanceCharge[] getItemCharges();
/**
* The price of one item excl. taxes
*
* @return The price of one item excl. taxes
*/
BigDecimal getPrice();
/**
* how many
*
* @return the quantity of the item
*/
BigDecimal getQuantity();
default String getCategoryCode() {
return TaxCategoryCodeTypeConstants.STANDARDRATE;
}
default String getAdditionalReferencedDocumentID() {
return null;
}
}

View File

@@ -0,0 +1,99 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
/**
* Mustangproject's ZUGFeRD implementation
* Necessary interface for ZUGFeRD exporter
* Licensed under the APLv2
*
* @author jstaerk
* @version 1.2.0
* dated 2014-05-10
*/
public interface IZUGFeRDExportableProduct {
/**
* Unit code of the product
* Most common ones are
* C62 one (piece)
* DAY day
* HAR hectare
* HUR hour
* KGM kilogram
* KTM kilometre
* KWH kilowatt hour
* LS lump sum
* LTR litre
* MIN minute
* MMK square millimetre
* MMT millimetre
* MTK square metre
* MTQ cubic metre
* MTR metre
* NAR number of articles
* NPR number of pairs
* P1 percent
* SET set
* TNE tonne (metric ton)
* WEE week
*
* @return a UN/ECE rec 20 unit code see https://www.unece.org/fileadmin/DAM/cefact/recommendations/rec20/rec20_rev3_Annex2e.pdf
*/
String getUnit();
/**
* Short name of the product
*
* @return Short name of the product
*/
String getName();
/**
* long description of the product
*
* @return long description of the product
*/
String getDescription();
/**
* VAT percent of the product (e.g. 19, or 5.1 if you like)
*
* @return VAT percent of the product
*/
BigDecimal getVATPercent();
default boolean isIntraCommunitySupply() {
return false;
}
default String getTaxCategoryCode() {
if (isIntraCommunitySupply()) {
return "K";
} else {
return "S";
}
}
}

View File

@@ -0,0 +1,384 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation
* Neccessary interface for ZUGFeRD exporter
* Licensed under the APLv2
* @date 2014-05-10 to 2014-06-25
* @version 1.2.0
* @author jstaerk
* */
import java.math.BigDecimal;
import java.util.Date;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
public interface IZUGFeRDExportableTransaction {
/**
* appears in /rsm:CrossIndustryDocument/rsm:HeaderExchangedDocument/ram:Name
*
* @return Name of document
*/
default String getDocumentName() {
return "RECHNUNG";
}
/**
*
*
* @return Code of Document
*/
default String getDocumentCode() {
return DocumentCodeTypeConstants.INVOICE;
}
/**
* Number, typically invoice number of the invoice
*
* @return invoice number
*/
default String getNumber() {
return null;
}
/**
* the date when the invoice was created
*
* @return when the invoice was created
*/
default Date getIssueDate() {
return null;
}
/**
* this should be the full sender institution name, details, manager and tax registration. It is one of the few functions which may return null. e.g.
* <p>
* Lieferant GmbH Lieferantenstraße 20 80333 München Deutschland Geschäftsführer: Hans Muster Handelsregisternummer: H A 123
*
* @return null or full sender institution name, details, manager and tax registration
*/
default String getOwnOrganisationFullPlaintextInfo() {
return null;
}
/**
* when the invoice is to be paid
*
* @return when the invoice is to be paid
*/
default Date getDueDate() {
return null;
}
/**
* who processed the order
*
* @return the contact person at the supplier side
*/
default IZUGFeRDExportableContact getOwnContact() {
return null;
}
IZUGFeRDAllowanceCharge[] getZFAllowances();
IZUGFeRDAllowanceCharge[] getZFCharges();
IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges();
IZUGFeRDExportableItem[] getZFItems();
/**
* the recipient
*
* @return the recipient of the invoice
*/
IZUGFeRDExportableContact getRecipient();
/**
* the creditors payment informations
* @deprecated use getTradeSettlement
* @return an array of IZUGFeRDTradeSettlementPayment
*/
@Deprecated
default IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment() {
return null;
}
/**
* the payment information for any payment means
*
* @return an array of IZUGFeRDTradeSettlement
*/
default IZUGFeRDTradeSettlement[] getTradeSettlement() {
return null;
}
/**
* Tax ID (not VAT ID) of the sender
*
* @return Tax ID (not VAT ID) of the sender
*/
default String getOwnTaxID() {
return null;
}
/**
* VAT ID (Umsatzsteueridentifikationsnummer) of the sender
*
* @return VAT ID (Umsatzsteueridentifikationsnummer) of the sender
*/
default String getOwnVATID() {
return null;
}
/**
* supplier identification assigned by the costumer
*
* @return the sender's identification
*/
default String getOwnForeignOrganisationID() {
return null;
}
/**
* own name
*
* @return the sender's organisation name
*/
default String getOwnOrganisationName() {
return null;
}
/**
* own street address
*
* @return sender street address
*/
default String getOwnStreet() {
return null;
}
/**
* own street postal code
*
* @return sender postal code
*/
default String getOwnZIP() {
return null;
}
/**
* own city
*
* @return the invoice sender's city
*/
default String getOwnLocation() {
return null;
}
/**
* own two digit country code
*
* @return the invoice senders two character country iso code
*/
default String getOwnCountry() {
return null;
}
/**
* get delivery date
*
* @return the day the goods have been delivered
*/
Date getDeliveryDate();
/**
* get delivery date in more specific form. If this and getDeliveryDate() are
* specified, this value will be taken
*
* @return instance holding date and format
*/
default IZUGFeRDDate getZFDeliveryDate() {
return null;
}
/**
* get main invoice currency used on the invoice
*
* @return three character currency of this invoice
*/
default String getCurrency() {
return null;
}
/**
* get payment term descriptional text e.g. Bis zum 22.10.2015 ohne Abzug
*
* @return get payment terms
*/
default String getPaymentTermDescription() {
return null;
}
/**
* get payment terms. if set, getPaymentTermDescription() and getDueDate() are
* ignored
*
* @return
*/
default IZUGFeRDPaymentTerms getPaymentTerms() {
return null;
}
/**
* get reference document number typically used for Invoice Corrections Will be added as IncludedNote in comfort profile
*
* @return the ID of the document this document refers to
*/
default String getReferenceNumber() {
return null;
}
/**
* consignee identification (identification of the organisation the goods are shipped to [assigned by the costumer])
*
* @return the sender's identification
*/
default String getShipToOrganisationID() {
return null;
}
/**
* consignee name (name of the organisation the goods are shipped to)
*
* @return the consignee's organisation name
*/
default String getShipToOrganisationName() {
return null;
}
/**
* consignee street address (street of the organisation the goods are shipped to)
*
* @return consignee street address
*/
default String getShipToStreet() {
return null;
}
/**
* consignee street postal code (postal code of the organisation the goods are shipped to)
*
* @return consignee postal code
*/
default String getShipToZIP() {
return null;
}
/**
* consignee city (city of the organisation the goods are shipped to)
*
* @return the consignee's city
*/
default String getShipToLocation() {
return null;
}
/**
* consignee two digit country code (country code of the organisation the goods are shipped to)
*
* @return the consignee's two character country iso code
*/
default String getShipToCountry() {
return null;
}
/**
* get the ID of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
*
* @return the ID of the document
*/
default String getBuyerOrderReferencedDocumentID() {
return null;
}
/**
* get the issue timestamp of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
*
* @return the IssueDateTime in format CCYY-MM-DDTHH:MM:SS
*/
default String getBuyerOrderReferencedDocumentIssueDateTime() {
return null;
}
/**
* get the TotalPrepaidAmount located in SpecifiedTradeSettlementMonetarySummation (v1) or SpecifiedTradeSettlementHeaderMonetarySummation (v2)
*
* @return the total sum (incl. VAT) of prepayments, i.e. the difference between GrandTotalAmount and DuePayableAmount
*/
default BigDecimal getTotalPrepaidAmount() {
return BigDecimal.ZERO;
}
/***
* delivery address, i.e. ram:ShipToTradeParty (only supported for zf2)
* @return
*/
default IZUGFeRDExportableContact getDeliveryAddress() {
return null;
}
}

View File

@@ -0,0 +1,15 @@
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
public interface IZUGFeRDPaymentDiscountTerms {
BigDecimal getCalculationPercentage();
IZUGFeRDDate getBaseDate();
int getBasePeriodMeasure();
String getBasePeriodUnitCode();
}

View File

@@ -0,0 +1,10 @@
package org.mustangproject.ZUGFeRD;
public interface IZUGFeRDPaymentTerms {
String getDescription();
IZUGFeRDDate getDueDate();
IZUGFeRDPaymentDiscountTerms getDiscountTerms();
}

View File

@@ -0,0 +1,40 @@
/** **********************************************************************
*
* Copyright 2019 by ak on 12.04.19.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public interface IZUGFeRDTradeSettlement {
/***
*
* @return zf2 xml for applicableHeaderTradeSettlement
*/
String getSettlementXML();
/***
*
* @return zf2 xml for applicableHeaderTradePayment
*/
default String getPaymentXML() {
return null;
}
}

View File

@@ -0,0 +1,57 @@
/** **********************************************************************
*
* Copyright 2019 by ak on 12.04.19.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import org.mustangproject.XMLTools;
public interface IZUGFeRDTradeSettlementDebit extends IZUGFeRDTradeSettlement {
default String getSettlementXML() {
String xml = " <ram:SpecifiedTradeSettlementPaymentMeans>\n" //$NON-NLS-1$
+ " <ram:TypeCode>59</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:PayerPartyDebtorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:IBANID>"+XMLTools.encodeXML(getIBAN())+"</ram:IBANID>\n" //$NON-NLS-1$
+ " </ram:PayerPartyDebtorFinancialAccount>\n"; //$NON-NLS-1$
xml = xml + " </ram:SpecifiedTradeSettlementPaymentMeans>\n"; //$NON-NLS-1$
return xml;
}
default String getPaymentXML() {
return "<ram:DirectDebitMandateID>"+XMLTools.encodeXML(getMandate())+"</ram:DirectDebitMandateID>";
}
/***
* @return IBAN of the debtor (optional)
*/
String getIBAN();
/***
* @return sepa direct debit mandate reference
*/
String getMandate();
}

View File

@@ -0,0 +1,111 @@
/** **********************************************************************
*
* Copyright 2019 by ak on 12.04.19.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.text.SimpleDateFormat;
import org.mustangproject.XMLTools;
public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement {
/**
* get payment information text. e.g. Bank transfer
*
* @return payment information text
*/
default String getOwnPaymentInfoText() {
return null;
}
/**
* BIC of the sender
*
* @return the BIC code of the recipient sender's bank
*/
default String getOwnBIC() {
return null;
}
/**
* BLZ of the sender
*
* @return the BLZ code of the recipient sender's bank
*/
default String getOwnBLZ() {
return null;
}
/**
* Bank name of the sender
*
* @return the name of the sender's bank
*/
default String getOwnBankName() {
return null;
}
/**
* IBAN of the sender
*
* @return the IBAN of the invoice sender's bank account
*/
default String getOwnIBAN() {
return null;
}
/**
* IBAN of the sender
*
* @return the Account Number of the invoice sender's bank account
*/
default String getOwnKto() {
return null;
}
default String getSettlementXML() {
String xml = " <ram:SpecifiedTradeSettlementPaymentMeans>\n" //$NON-NLS-1$
+ " <ram:TypeCode>42</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:Information>Überweisung</ram:Information>\n" //$NON-NLS-1$
+ " <ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:IBANID>" + XMLTools.encodeXML(getOwnIBAN()) + "</ram:IBANID>\n"; //$NON-NLS-1$ //$NON-NLS-2$
if (getOwnKto()!=null) {
xml+= " <ram:ProprietaryID>" + XMLTools.encodeXML(getOwnKto()) + "</ram:ProprietaryID>\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
xml+= " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " <ram:BICID>" + XMLTools.encodeXML(getOwnBIC()) + "</ram:BICID>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <ram:Name>"+trans.getOwnBankName()+"</ram:Name>\n" //$NON-NLS-1$
// //$NON-NLS-2$
+ " </ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTradeSettlementPaymentMeans>\n"; //$NON-NLS-1$
return xml;
}
/* I'd love to implement getPaymentXML() and put <ram:DueDateDateTime> there because this is where it belongs
* unfortunately, the due date is part of the transaction which is not accessible here :-(
*/
}

View File

@@ -0,0 +1,58 @@
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import java.math.BigDecimal;
public class Item implements IZUGFeRDExportableItem {
protected BigDecimal price, quantity;
protected Product product;
public Item(Product product, BigDecimal price, BigDecimal quantity) {
this.price = price;
this.quantity = quantity;
this.product = product;
}
@Override
public BigDecimal getPrice() {
return price;
}
public Item setPrice(BigDecimal price) {
this.price = price;
return this;
}
@Override
public BigDecimal getQuantity() {
return quantity;
}
public Item setQuantity(BigDecimal quantity) {
this.quantity = quantity;
return this;
}
@Override
public Product getProduct() {
return product;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemCharges() {
return null;
}
public Item setProduct(Product product) {
this.product = product;
return this;
}
}

View File

@@ -0,0 +1,42 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public enum PDFAConformanceLevel {
ACCESSIBLE("A"), BASIC("B"), UNICODE("U");
private final String letter;
PDFAConformanceLevel(String letter) {
this.letter = letter;
}
public String getLetter() {
return letter;
}
public static PDFAConformanceLevel findByLetter(String letter) {
for (PDFAConformanceLevel candidate : values()) {
if (candidate.letter.equals(letter)) {
return candidate;
}
}
throw new IllegalArgumentException("PDF conformance level <" + letter + "> is unknown.");
}
}

View File

@@ -0,0 +1,59 @@
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import java.math.BigDecimal;
public class Product implements IZUGFeRDExportableProduct {
protected String unit, name, description;
protected BigDecimal VATPercent;
public Product(String name, String description, String unit, BigDecimal VATPercent) {
this.unit = unit;
this.name = name;
this.description = description;
this.VATPercent = VATPercent;
}
@Override
public String getUnit() {
return unit;
}
public Product setUnit(String unit) {
this.unit = unit;
return this;
}
@Override
public String getName() {
return name;
}
public Product setName(String name) {
this.name = name;
return this;
}
@Override
public String getDescription() {
return description;
}
public Product setDescription(String description) {
this.description = description;
return this;
}
@Override
public BigDecimal getVATPercent() {
return VATPercent;
}
public Product setVATPercent(BigDecimal VATPercent) {
this.VATPercent = VATPercent;
return this;
}
}

View File

@@ -0,0 +1,96 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
/**
* Mustangproject's ZUGFeRD implementation
* ZUGFeRD exporter helper class
* Licensed under the APLv2
*
* @author jstaerk
* @version 1.2.0
* dated 2015-10-29
*/
public class VATAmount {
public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) {
super();
this.basis = basis;
this.calculated = calculated;
this.categoryCode = categoryCode;
}
BigDecimal basis, calculated;
String categoryCode;
public BigDecimal getBasis() {
return basis;
}
public void setBasis(BigDecimal basis) {
this.basis = basis;
}
public BigDecimal getCalculated() {
return calculated;
}
public void setCalculated(BigDecimal calculated) {
this.calculated = calculated;
}
/**
*
* @deprecated Use {@link #getCategoryCode() instead}
* @return String with category code
*/
@Deprecated
public String getDocumentCode() {
return categoryCode;
}
/**
* @param documentCode as String
* @deprecated Use {@link #setCategoryCode(String)} instead
*/
@Deprecated
public void setDocumentCode(String documentCode) {
this.categoryCode = documentCode;
}
public String getCategoryCode() {
return categoryCode;
}
public void setCategoryCode(String categoryCode) {
this.categoryCode = categoryCode;
}
public VATAmount add(VATAmount v) {
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode);
}
public VATAmount subtract(VATAmount v) {
return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.categoryCode);
}
}

View File

@@ -0,0 +1,140 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation
* ZUGFeRD exporter helper class
* Licensed under the APLv2
* @date 2014-05-10
* @version 1.2.0
* @author jstaerk
* */
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.XmpConstants;
import org.apache.xmpbox.schema.PDFAExtensionSchema;
import org.apache.xmpbox.type.*;
/**
* Additionally to adding a RDF namespace with a indication which file
* attachment if Zugferd, this namespace has to be described in a PDFA Extension
* Schema. I know there is a PDFAExtensionSchema in the context of PDFBox'
* XMPBOX but I have been using PDFBox' JempBOX so far because I could not find
* out how to write XMPBOX XMPMetadata to a PDF file. So this is my version of
* PDFAExtensionSchema for PDFBox' jempbox XMPMetadata
*
* @author jstaerk
*/
@StructuredType(preferedPrefix = "pdfaExtension", namespace = "http://www.aiim.org/pdfa/ns/extension/")
public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
public final String xmlns_pdfaSchema = "http://www.aiim.org/pdfa/ns/schema#";
public final String prefix_pdfaSchema = "pdfaSchema";
public final String xmlns_pdfaProperty = "http://www.aiim.org/pdfa/ns/property#";
public final String prefix_pdfaProperty = "pdfaProperty";
public String namespace = null;
public String prefix = null;
protected ZUGFeRDExporter exporter;
protected void setZUGFeRDVersion(int ver) {
namespace = exporter.getNamespaceForVersion(ver);
prefix = exporter.getPrefixForVersion(ver);
}
private DefinedStructuredType addProperty(ArrayProperty parent, String name, String type, String category,
String description) {
XMPMetadata metadata = getMetadata();
DefinedStructuredType li = new DefinedStructuredType(metadata, getNamespace(), getPrefix(),
XmpConstants.LIST_NAME);
li.setAttribute(new Attribute(getNamespace(), XmpConstants.PARSE_TYPE, XmpConstants.RESOURCE_NAME));
ChoiceType pdfa2 = new ChoiceType(metadata, xmlns_pdfaProperty, prefix_pdfaProperty, PDFAPropertyType.NAME,
name);
li.addProperty(pdfa2);
pdfa2 = new ChoiceType(metadata, xmlns_pdfaProperty, prefix_pdfaProperty, PDFAPropertyType.VALUETYPE, type);
li.addProperty(pdfa2);
pdfa2 = new ChoiceType(metadata, xmlns_pdfaProperty, prefix_pdfaProperty, PDFAPropertyType.CATEGORY, category);
li.addProperty(pdfa2);
pdfa2 = new ChoiceType(metadata, xmlns_pdfaProperty, prefix_pdfaProperty, PDFAPropertyType.DESCRIPTION,
description);
li.addProperty(pdfa2);
parent.addProperty(li);
return li;
}
public XMPSchemaPDFAExtensions(ZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion) {
super(metadata);
exporter=ze;
setZUGFeRDVersion(ZFVersion);
attachExtensions(metadata, true);
}
public XMPSchemaPDFAExtensions(ZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion, boolean withZF) {
super(metadata);
exporter=ze;
setZUGFeRDVersion(ZFVersion);
attachExtensions(metadata, withZF);
}
public void attachExtensions(XMPMetadata metadata, boolean withZF) {
addNamespace(xmlns_pdfaSchema, prefix_pdfaSchema);
addNamespace(xmlns_pdfaProperty, prefix_pdfaProperty);
ArrayProperty newBag = createArrayProperty(SCHEMAS, Cardinality.Bag);
DefinedStructuredType li = new DefinedStructuredType(metadata, getNamespace(), getPrefix(),
XmpConstants.LIST_NAME);
li.setAttribute(new Attribute(getNamespace(), XmpConstants.PARSE_TYPE, XmpConstants.RESOURCE_NAME));
newBag.addProperty(li);
addProperty(newBag);
if (withZF) {
TextType pdfa1 = new TextType(metadata, xmlns_pdfaSchema, prefix_pdfaSchema, PDFASchemaType.SCHEMA,
"ZUGFeRD PDFA Extension Schema");
li.addProperty(pdfa1);
pdfa1 = new TextType(metadata, xmlns_pdfaSchema, prefix_pdfaSchema, PDFASchemaType.NAMESPACE_URI,
namespace);
li.addProperty(pdfa1);
pdfa1 = new TextType(metadata, xmlns_pdfaSchema, prefix_pdfaSchema, PDFASchemaType.PREFIX, prefix);
li.addProperty(pdfa1);
ArrayProperty newSeq = new ArrayProperty(metadata, xmlns_pdfaSchema, prefix_pdfaSchema,
PDFASchemaType.PROPERTY, Cardinality.Seq);
li.addProperty(newSeq);
addProperty(newSeq, "DocumentFileName", "Text", "external", "name of the embedded XML invoice file");
addProperty(newSeq, "DocumentType", "Text", "external", "INVOICE");
addProperty(newSeq, "Version", "Text", "external", "The actual version of the ZUGFeRD XML schema");
addProperty(newSeq, "ConformanceLevel", "Text", "external",
"The selected ZUGFeRD profile completeness");
}
}
}

View File

@@ -0,0 +1,64 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation
* ZUGFeRD exporter helper class
* Licensed under the APLv2
* @date 2014-05-10
* @version 1.2.0s
* @author jstaerk
* */
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.XMPSchema;
public class XMPSchemaZugferd extends XMPSchema {
/***
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file
* @param metadata the xmp to be added to
* @param zfVersion which ZF version to use (2 for FX)
* @param isFacturX whether to export as Factur-X
* @param conformanceLevel e.g. conformanceLevel.EN16931
* @param URN the xml URI for the XMP
* @param prefix the xml namespace prefix for the XMP, zf for ZUGFeRD, fx for Factur-X
* @param filename the filename of the invoice
*/
public XMPSchemaZugferd(XMPMetadata metadata, int zfVersion, boolean isFacturX, ZUGFeRDConformanceLevel conformanceLevel, String URN, String prefix, String filename) {
super(metadata, URN, prefix, "ZUGFeRD Schema");
setAboutAsSimple("");
String conformanceLevelValue = conformanceLevel.name();
if (conformanceLevelValue.equals("BASICWL")) {
conformanceLevelValue = "BASIC WL";
} else if (conformanceLevelValue.equals("EN16931")) {
conformanceLevelValue = "EN 16931";
}
setTextPropertyValue("ConformanceLevel", conformanceLevelValue);
setTextPropertyValue("DocumentType", "INVOICE");
setTextPropertyValue("DocumentFileName", filename);
String version="1.0";
if ((zfVersion==2)&&(!isFacturX)) {
version="2p0";
}
setTextPropertyValue("Version", version);
}
}

View File

@@ -0,0 +1,119 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import org.mustangproject.ZUGFeRD.model.ZFNamespacePrefixMapper;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.ByteArrayOutputStream;
public class ZUGFeRD1PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
private Marshaller marshaller;
private boolean isTest;
private ZUGFeRDConformanceLevel level;
public void setProfile(ZUGFeRDConformanceLevel level) {
this.level = level;
}
public String getProfile() {
switch (level) {
case BASIC: return DocumentContextParameterTypeConstants.BASIC;
case COMFORT: return DocumentContextParameterTypeConstants.COMFORT;
default: return DocumentContextParameterTypeConstants.EXTENDED;
}
}
/**
* enables the flag to indicate a test invoice in the XML structure
*/
public void setTest() {
isTest = true;
}
public ZUGFeRD1PullProvider() {
// TODO Auto-generated constructor stub
try {
marshaller = JAXBContext.newInstance("org.mustangproject.ZUGFeRD.model").createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new ZFNamespacePrefixMapper());
} catch (JAXBException e) {
throw new ZUGFeRDExportException("Could not initialize JAXB", e);
}
}
private String createZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) {
JAXBElement<CrossIndustryDocumentType> jaxElement =
new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).withProfile(getProfile()).convertToModel();
try {
return marshalJaxToXMLString(jaxElement);
} catch (JAXBException e) {
throw new ZUGFeRDExportException("Could not marshal ZUGFeRD transaction to XML", e);
}
}
private String marshalJaxToXMLString(Object jaxElement) throws JAXBException {
ByteArrayOutputStream outputXml = new ByteArrayOutputStream();
marshaller.marshal(jaxElement, outputXml);
return outputXml.toString();
}
@Override
public byte[] getXML() {
return zugferdData;
}
@Override
public void generateXML(IZUGFeRDExportableTransaction trans) {
// create a dummy file stream, this would probably normally be a
// FileInputStream
byte[] zugferdRaw = createZugferdXMLForTransaction(trans).getBytes(); //$NON-NLS-1$
if ((zugferdRaw[0] == (byte) 0xEF)
&& (zugferdRaw[1] == (byte) 0xBB)
&& (zugferdRaw[2] == (byte) 0xBF)) {
// I don't like BOMs, lets remove it
zugferdData = new byte[zugferdRaw.length - 3];
System.arraycopy(zugferdRaw, 3, zugferdData, 0,
zugferdRaw.length - 3);
} else {
zugferdData = zugferdRaw;
}
}
}

View File

@@ -0,0 +1,624 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.mustangproject.XMLTools;
public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
private class LineCalc {
private BigDecimal totalGross;
private BigDecimal priceGross;
private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount;
public LineCalc(IZUGFeRDExportableItem currentItem) {
BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100))
.add(new BigDecimal(1));
priceGross = currentItem.getPrice().multiply(multiplicator);
totalGross = currentItem.getPrice().multiply(multiplicator).multiply(currentItem.getQuantity());
itemTotalNetAmount = currentItem.getQuantity().multiply(currentItem.getPrice()).setScale(2,
BigDecimal.ROUND_HALF_UP);
itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount);
}
public BigDecimal getItemTotalNetAmount() {
return itemTotalNetAmount;
}
public BigDecimal getItemTotalVATAmount() {
return itemTotalVATAmount;
}
public BigDecimal getItemTotalGrossAmount() {
return itemTotalVATAmount;
}
public BigDecimal getPriceGross() {
return priceGross;
}
}
//// MAIN CLASS
protected byte[] zugferdData;
private IZUGFeRDExportableTransaction trans;
private ZUGFeRDConformanceLevel level;
private String paymentTermsDescription;
@Override
public void setProfile(ZUGFeRDConformanceLevel level) {
this.level = level;
}
/**
* enables the flag to indicate a test invoice in the XML structure
*/
@Override
public void setTest() {
}
public static String nDigitFormat(BigDecimal value, int scale) {
/*
* I needed 123,45, locale independent.I tried
* NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale
* specific.I also tried DecimalFormat df = new DecimalFormat( "0,00" );
* df.setDecimalSeparatorAlwaysShown(true); df.setGroupingUsed(false);
* DecimalFormatSymbols symbols = new DecimalFormatSymbols();
* symbols.setDecimalSeparator(','); symbols.setGroupingSeparator(' ');
* df.setDecimalFormatSymbols(symbols);
*
* but that would not switch off grouping. Although I liked very much the
* (incomplete) "BNF diagram" in
* http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html in the
* end I decided to calculate myself and take eur+sparator+cents
*
*/
return value.setScale(scale, RoundingMode.HALF_UP).toPlainString();
}
private String vatFormat(BigDecimal value) {
return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String currencyFormat(BigDecimal value) {
return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String priceFormat(BigDecimal value) {
return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
private String quantityFormat(BigDecimal value) {
return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
@Override
public byte[] getXML() {
byte[] res = zugferdData;
StringWriter sw = new StringWriter();
Document document = null;
try {
document = DocumentHelper.parseText(new String(zugferdData));
} catch (DocumentException e1) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e1);
}
try {
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(sw, format);
writer.write(document);
res = sw.toString().getBytes("UTF-8");
} catch (IOException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e);
}
return res;
}
private BigDecimal getTotalPrepaid() {
if (trans.getTotalPrepaidAmount() == null) {
return new BigDecimal(0);
} else {
return trans.getTotalPrepaidAmount();
}
}
private BigDecimal getTotalGross() {
BigDecimal res = getTotal();
HashMap<BigDecimal, VATAmount> VATPercentAmountMap = getVATPercentAmountMap();
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
res = res.add(amount.getCalculated());
}
return res;
}
private BigDecimal getTotal() {
BigDecimal res = new BigDecimal(0);
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
LineCalc lc = new LineCalc(currentItem);
res = res.add(lc.getItemTotalNetAmount());
}
return res;
}
/**
* which taxes have been used with which amounts in this transaction, empty for
* no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable to 19% VAT
* (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT) 190 Eur
*
* @return which taxes have been used with which amounts in this invoice
*/
private HashMap<BigDecimal, VATAmount> getVATPercentAmountMap() {
HashMap<BigDecimal, VATAmount> hm = new HashMap<>();
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalc lc = new LineCalc(currentItem);
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
trans.getDocumentCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
} else {
hm.put(percent, current.add(itemVATAmount));
}
}
return hm;
}
@Override
public String getProfile() {
// return "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_1.2";
return "urn:cen.eu:en16931:2017";
}
protected String getContactAsXML(IZUGFeRDExportableContact contact) {
String xml = " <ram:Name>" + XMLTools.encodeXML(contact.getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <DefinedTradeContact>\n"
// + " <PersonName>xxx</PersonName>\n"
// + " </DefinedTradeContact>\n"
+ " <ram:PostalTradeAddress>\n" //$NON-NLS-1$
+ " <ram:PostcodeCode>" + XMLTools.encodeXML(contact.getZIP()) //$NON-NLS-1$
+ "</ram:PostcodeCode>\n" //$NON-NLS-1$
+ " <ram:LineOne>" + XMLTools.encodeXML(contact.getStreet()) //$NON-NLS-1$
+ "</ram:LineOne>\n"; //$NON-NLS-1$
if (trans.getRecipient().getAdditionalAddress() != null) {
xml += " <ram:LineTwo>" + XMLTools.encodeXML(contact.getAdditionalAddress()) //$NON-NLS-1$
+ "</ram:LineTwo>\n"; //$NON-NLS-1$
}
xml += " <ram:CityName>" + XMLTools.encodeXML(contact.getLocation()) //$NON-NLS-1$
+ "</ram:CityName>\n" //$NON-NLS-1$
+ " <ram:CountryID>" + XMLTools.encodeXML(contact.getCountry()) //$NON-NLS-1$
+ "</ram:CountryID>\n" //$NON-NLS-1$
+ " </ram:PostalTradeAddress>\n"; //$NON-NLS-1$
if (contact.getVATID() != null) {
xml += " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(contact.getVATID()) //$NON-NLS-1$
+ "</ram:ID>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTaxRegistration>\n"; //$NON-NLS-1$
}
return xml;
}
@Override
public void generateXML(IZUGFeRDExportableTransaction trans) {
this.trans = trans;
boolean hasDueDate=false;
String taxCategoryCode="";
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); //$NON-NLS-1$
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
String exemptionReason="";
if (trans.getPaymentTermDescription()!=null) {
paymentTermsDescription=trans.getPaymentTermDescription();
}
if (paymentTermsDescription==null) {
paymentTermsDescription= "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate());
}
String senderReg = "";
if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
senderReg = "" + "<ram:IncludedNote>\n" + " <ram:Content>\n"
+ XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + " </ram:Content>\n"
+ "<ram:SubjectCode>REG</ram:SubjectCode>\n" + "</ram:IncludedNote>\n";
}
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
+ "<rsm:CrossIndustryInvoice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:rsm=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100\""
// + "
// xsi:schemaLocation=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100
// ../Schema/ZUGFeRD1p0.xsd\""
+ " xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\""
+ " xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\">\n" //$NON-NLS-1$
+ " <rsm:ExchangedDocumentContext>\n" //$NON-NLS-1$
// + "
// <ram:TestIndicator><udt:Indicator>"+testBooleanStr+"</udt:Indicator></ram:TestIndicator>\n"
// //$NON-NLS-1$
+ " <ram:GuidelineSpecifiedDocumentContextParameter>\n" //$NON-NLS-1$
+ " <ram:ID>" + getProfile() + "</ram:ID>\n" //$NON-NLS-1$
+ " </ram:GuidelineSpecifiedDocumentContextParameter>\n" //$NON-NLS-1$
+ " </rsm:ExchangedDocumentContext>\n" //$NON-NLS-1$
+ " <rsm:ExchangedDocument>\n" //$NON-NLS-1$
+ " <ram:ID>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <ram:Name>RECHNUNG</ram:Name>\n" //$NON-NLS-1$
+ " <ram:TypeCode>380</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:IssueDateTime><udt:DateTimeString format=\"102\">" //$NON-NLS-1$
+ zugferdDateFormat.format(trans.getIssueDate()) + "</udt:DateTimeString></ram:IssueDateTime>\n" // date //$NON-NLS-1$
// format
// was
// 20130605
+ senderReg
// + " <IncludedNote>\n"
// + " <Content>\n"
// + "Rechnung gemäß Bestellung Nr. 2013-471331 vom 01.03.2013.\n"
// + "\n"
// + " </Content>\n"
// + " </IncludedNote>\n"
// + " <IncludedNote>\n"
// + " <Content>\n"
// + "Es bestehen Rabatt- und Bonusvereinbarungen.\n"
// + " </Content>\n"
// + " <SubjectCode>AAK</SubjectCode>\n"
// + " </IncludedNote>\n"
+ " </rsm:ExchangedDocument>\n" //$NON-NLS-1$
+ " <rsm:SupplyChainTradeTransaction>\n"; //$NON-NLS-1$
int lineID = 0;
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
lineID++;
taxCategoryCode=currentItem.getProduct().getTaxCategoryCode();
if (currentItem.getProduct().isIntraCommunitySupply()) {
exemptionReason="<ram:ExemptionReason>Intra-community supply</ram:ExemptionReason>";
}
LineCalc lc = new LineCalc(currentItem);
xml = xml + " <ram:IncludedSupplyChainTradeLineItem>\n" + //$NON-NLS-1$
" <ram:AssociatedDocumentLineDocument>\n" //$NON-NLS-1$
+ " <ram:LineID>" + lineID + "</ram:LineID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:AssociatedDocumentLineDocument>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTradeProduct>\n" //$NON-NLS-1$
// + " <GlobalID schemeID=\"0160\">4012345001235</GlobalID>\n"
// + " <SellerAssignedID>KR3M</SellerAssignedID>\n"
// + " <BuyerAssignedID>55T01</BuyerAssignedID>\n"
+ " <ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:Description>" + XMLTools.encodeXML(currentItem.getProduct().getDescription()) //$NON-NLS-1$
+ "</ram:Description>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTradeProduct>\n" //$NON-NLS-1$
+ " <ram:SpecifiedLineTradeAgreement>\n" //$NON-NLS-1$
+ " <ram:GrossPriceProductTradePrice>\n" //$NON-NLS-1$
+ " <ram:ChargeAmount>" + priceFormat(lc.getPriceGross()) //$NON-NLS-1$
+ "</ram:ChargeAmount>\n" //$NON-NLS-1$ //currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$
+ "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$
// + " <AppliedTradeAllowanceCharge>\n"
// + " <ChargeIndicator>false</ChargeIndicator>\n"
// + " <ActualAmount currencyID=\"EUR\">0.6667</ActualAmount>\n"
// + " <Reason>Rabatt</Reason>\n"
// + " </AppliedTradeAllowanceCharge>\n"
+ " </ram:GrossPriceProductTradePrice>\n" //$NON-NLS-1$
+ " <ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$
+ " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) //$NON-NLS-1$
+ "</ram:ChargeAmount>\n" //$NON-NLS-1$ // currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$
+ "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$
+ " </ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$
+ " </ram:SpecifiedLineTradeAgreement>\n" //$NON-NLS-1$
+ " <ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$
+ " <ram:BilledQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + "\">" //$NON-NLS-1$ //$NON-NLS-2$
+ quantityFormat(currentItem.getQuantity()) + "</ram:BilledQuantity>\n" //$NON-NLS-1$
+ " </ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$
+ " <ram:SpecifiedLineTradeSettlement>\n" //$NON-NLS-1$
+ " <ram:ApplicableTradeTax>\n" //$NON-NLS-1$
+ " <ram:TypeCode>VAT</ram:TypeCode>\n" //$NON-NLS-1$
+ exemptionReason
+ " <ram:CategoryCode>"+currentItem.getProduct().getTaxCategoryCode()+"</ram:CategoryCode>\n" //$NON-NLS-1$
+ " <ram:RateApplicablePercent>" //$NON-NLS-1$
+ vatFormat(currentItem.getProduct().getVATPercent()) + "</ram:RateApplicablePercent>\n" //$NON-NLS-1$
+ " </ram:ApplicableTradeTax>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTradeSettlementLineMonetarySummation>\n" //$NON-NLS-1$
+ " <ram:LineTotalAmount>" + currencyFormat(lc.getItemTotalNetAmount()) //$NON-NLS-1$
+ "</ram:LineTotalAmount>\n" //$NON-NLS-1$ // currencyID=\"EUR\"
+ " </ram:SpecifiedTradeSettlementLineMonetarySummation>\n"; //$NON-NLS-1$
if (currentItem.getAdditionalReferencedDocumentID()!=null) {
xml=xml + " <ram:AdditionalReferencedDocument><ram:IssuerAssignedID>"+currentItem.getAdditionalReferencedDocumentID()+"</ram:IssuerAssignedID><ram:TypeCode>130</ram:TypeCode></ram:AdditionalReferencedDocument>\n"; //$NON-NLS-1$
}
xml=xml + " </ram:SpecifiedLineTradeSettlement>\n" //$NON-NLS-1$
+ " </ram:IncludedSupplyChainTradeLineItem>\n"; //$NON-NLS-1$
}
xml = xml + " <ram:ApplicableHeaderTradeAgreement>\n"; //$NON-NLS-1$
if (trans.getReferenceNumber() != null) {
xml = xml + " <ram:BuyerReference>" + XMLTools.encodeXML(trans.getReferenceNumber()) + "</ram:BuyerReference>\n";
}
xml = xml + " <ram:SellerTradeParty>\n" //$NON-NLS-1$
// + " <GlobalID schemeID=\"0088\">4000001123452</GlobalID>\n"
+ " <ram:Name>" + XMLTools.encodeXML(trans.getOwnOrganisationName()) + "</ram:Name>\n"; //$NON-NLS-1$ //$NON-NLS-2$
if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) {
xml = xml + " <ram:SpecifiedLegalOrganization>\n" + " <ram:ID>"
+ XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" + " <ram:TradingBusinessName>"
+ XMLTools.encodeXML(trans.getOwnOrganisationName()) + "</ram:TradingBusinessName>\n"
+ " </ram:SpecifiedLegalOrganization>";
}
if (trans.getOwnContact() != null) {
xml = xml + "<ram:DefinedTradeContact>\n" + " <ram:PersonName>" + XMLTools.encodeXML(trans.getOwnContact().getName())
+ "</ram:PersonName>\n";
if (trans.getOwnContact().getPhone() != null) {
xml = xml + " <ram:TelephoneUniversalCommunication>\n" + " <ram:CompleteNumber>"
+ XMLTools.encodeXML(trans.getOwnContact().getPhone()) + "</ram:CompleteNumber>\n"
+ " </ram:TelephoneUniversalCommunication>\n";
}
if (trans.getOwnContact().getEMail() != null) {
xml = xml + " <ram:EmailURIUniversalCommunication>\n" + " <ram:URIID>"
+ XMLTools.encodeXML(trans.getOwnContact().getEMail()) + "</ram:URIID>\n"
+ " </ram:EmailURIUniversalCommunication>\n";
}
xml = xml + " </ram:DefinedTradeContact>";
}
xml = xml + " <ram:PostalTradeAddress>\n" + " <ram:PostcodeCode>"
+ XMLTools.encodeXML(trans.getOwnZIP()) + "</ram:PostcodeCode>\n" + " <ram:LineOne>"
+ XMLTools.encodeXML(trans.getOwnStreet()) + "</ram:LineOne>\n" + " <ram:CityName>" + XMLTools.encodeXML(trans.getOwnLocation())
+ "</ram:CityName>\n" + " <ram:CountryID>" + XMLTools.encodeXML(trans.getOwnCountry())
+ "</ram:CountryID>\n" + " </ram:PostalTradeAddress>\n"
+ " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"FC\">" + XMLTools.encodeXML(trans.getOwnTaxID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " </ram:SellerTradeParty>\n" //$NON-NLS-1$
+ " <ram:BuyerTradeParty>\n"; //$NON-NLS-1$
// + " <ID>GE2020211</ID>\n"
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
xml+=getContactAsXML(trans.getRecipient());
xml += " </ram:BuyerTradeParty>\n" //$NON-NLS-1$
// + " <BuyerOrderReferencedDocument>\n"
// + " <IssueDateTime format=\"102\">20130301</IssueDateTime>\n"
// + " <ID>2013-471331</ID>\n"
// + " </BuyerOrderReferencedDocument>\n"
+ " </ram:ApplicableHeaderTradeAgreement>\n" //$NON-NLS-1$
+ " <ram:ApplicableHeaderTradeDelivery>\n" ;
if (this.trans.getDeliveryAddress()!=null) {
xml += "<ram:ShipToTradeParty>"+
getContactAsXML(this.trans.getDeliveryAddress())+
"</ram:ShipToTradeParty>";
}
xml+= " <ram:ActualDeliverySupplyChainEvent>\n"
+ " <ram:OccurrenceDateTime>";
if (trans.getZFDeliveryDate() != null) {
ZUGFeRDDateFormat dateFormat = trans.getZFDeliveryDate().getFormat();
Date date = trans.getZFDeliveryDate().getDate();
xml += "<udt:DateTimeString format=\"" + dateFormat.getDateTimeType() + "\">"
+ dateFormat.getFormatter().format(date) + "</udt:DateTimeString>";
} else if (trans.getDeliveryDate() != null) {
xml += "<udt:DateTimeString format=\"102\">" + zugferdDateFormat.format(trans.getDeliveryDate())
+ "</udt:DateTimeString>";
} else {
throw new IllegalStateException("No delivery date provided");
}
xml += "</ram:OccurrenceDateTime>\n";
xml += " </ram:ActualDeliverySupplyChainEvent>\n"
/*
* + " <DeliveryNoteReferencedDocument>\n" +
* " <IssueDateTime format=\"102\">20130603</IssueDateTime>\n" +
* " <ID>2013-51112</ID>\n" +
* " </DeliveryNoteReferencedDocument>\n"
*/
+ " </ram:ApplicableHeaderTradeDelivery>\n" + " <ram:ApplicableHeaderTradeSettlement>\n" //$NON-NLS-2$
+ " <ram:PaymentReference>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:PaymentReference>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:InvoiceCurrencyCode>" + trans.getCurrency() + "</ram:InvoiceCurrencyCode>\n"; //$NON-NLS-1$
if (trans.getTradeSettlementPayment()!=null) {
for (IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) {
if(payment!=null) {
hasDueDate=true;
xml+=payment.getSettlementXML();
}
}
}
if (trans.getTradeSettlement()!=null) {
for (IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
if(payment!=null) {
if (payment instanceof IZUGFeRDTradeSettlementPayment) {
hasDueDate=true;
}
xml+=payment.getSettlementXML();
}
}
}
HashMap<BigDecimal, VATAmount> VATPercentAmountMap = getVATPercentAmountMap();
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
if (amount != null) {
xml += " <ram:ApplicableTradeTax>\n" //$NON-NLS-1$
+ " <ram:CalculatedAmount>" + currencyFormat(amount.getCalculated()) //$NON-NLS-1$
+ "</ram:CalculatedAmount>\n" //$NON-NLS-1$ //currencyID=\"EUR\"
+ " <ram:TypeCode>VAT</ram:TypeCode>\n" //$NON-NLS-1$
+ exemptionReason
+ " <ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>\n" // currencyID=\"EUR\"
+ " <ram:CategoryCode>"+taxCategoryCode+"</ram:CategoryCode>\n" //$NON-NLS-1$
+ " <ram:RateApplicablePercent>" + vatFormat(currentTaxPercent) //$NON-NLS-1$
+ "</ram:RateApplicablePercent>\n" + " </ram:ApplicableTradeTax>\n"; //$NON-NLS-2$
}
}
if (trans.getPaymentTerms() == null) {
xml = xml + " <ram:SpecifiedTradePaymentTerms>\n" //$NON-NLS-1$
+ " <ram:Description>" + paymentTermsDescription + "</ram:Description>\n";
if (trans.getTradeSettlement() != null) {
for (IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
if (payment != null) {
xml += payment.getPaymentXML();
}
}
}
if (hasDueDate) {
xml = xml + " <ram:DueDateDateTime><udt:DateTimeString format=\"102\">" // $NON-NLS-2$
+ zugferdDateFormat.format(trans.getDueDate())
+ "</udt:DateTimeString></ram:DueDateDateTime>\n";// 20130704 //$NON-NLS-1$
}
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n"; //$NON-NLS-1$
} else {
xml = xml + buildPaymentTermsXml();
}
xml = xml + " <ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$
+ " <ram:LineTotalAmount>" + currencyFormat(getTotal()) + "</ram:LineTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// currencyID=\"EUR\"
+ " <ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>\n" //$NON-NLS-1$ currencyID=\"EUR\"
+ " <ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>\n" //$NON-NLS-1$ //
// currencyID=\"EUR\"
// + " <ChargeTotalAmount currencyID=\"EUR\">5.80</ChargeTotalAmount>\n"
// + " <AllowanceTotalAmount currencyID=\"EUR\">14.73</AllowanceTotalAmount>\n"
+ " <ram:TaxBasisTotalAmount>" + currencyFormat(getTotal()) + "</ram:TaxBasisTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// //
// currencyID=\"EUR\"
+ " <ram:TaxTotalAmount currencyID=\"" + trans.getCurrency() + "\">" //$NON-NLS-1$
+ currencyFormat(getTotalGross().subtract(getTotal())) + "</ram:TaxTotalAmount>\n" //$NON-NLS-1$
+ " <ram:GrandTotalAmount>" + currencyFormat(getTotalGross()) + "</ram:GrandTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// //
// currencyID=\"EUR\"
+ " <ram:TotalPrepaidAmount>" + currencyFormat(getTotalPrepaid()) + "</ram:TotalPrepaidAmount>\n"
+ " <ram:DuePayableAmount>" + currencyFormat(getTotalGross().subtract(getTotalPrepaid())) + "</ram:DuePayableAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// //
// currencyID=\"EUR\"
+ " </ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$
+ " </ram:ApplicableHeaderTradeSettlement>\n"; //$NON-NLS-1$
// + " <IncludedSupplyChainTradeLineItem>\n"
// + " <AssociatedDocumentLineDocument>\n"
// + " <IncludedNote>\n"
// + " <Content>Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr.
// 2013-51112 in Rechnung zu stellen:</Content>\n"
// + " </IncludedNote>\n"
// + " </AssociatedDocumentLineDocument>\n"
// + " </IncludedSupplyChainTradeLineItem>\n";
xml = xml + " </rsm:SupplyChainTradeTransaction>\n" //$NON-NLS-1$
+ "</rsm:CrossIndustryInvoice>"; //$NON-NLS-1$
byte[] zugferdRaw;
try {
zugferdRaw = xml.getBytes("UTF-8");
if ((zugferdRaw[0] == (byte) 0xEF) && (zugferdRaw[1] == (byte) 0xBB) && (zugferdRaw[2] == (byte) 0xBF)) {
// I don't like BOMs, lets remove it
zugferdData = new byte[zugferdRaw.length - 3];
System.arraycopy(zugferdRaw, 3, zugferdData, 0, zugferdRaw.length - 3);
} else {
zugferdData = zugferdRaw;
}
} catch (UnsupportedEncodingException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e);
} // $NON-NLS-1$
}
private String buildPaymentTermsXml() {
String paymentTermsXml = "<ram:SpecifiedTradePaymentTerms>";
IZUGFeRDPaymentTerms paymentTerms = trans.getPaymentTerms();
IZUGFeRDPaymentDiscountTerms discountTerms = paymentTerms.getDiscountTerms();
IZUGFeRDDate dueDate = paymentTerms.getDueDate();
if (dueDate != null && discountTerms != null && discountTerms.getBaseDate() != null) {
throw new IllegalStateException(
"if paymentTerms.dueDate is specified, paymentTerms.discountTerms.baseDate has not to be specified");
}
paymentTermsXml += "<ram:Description>" + paymentTerms.getDescription() + "</ram:Description>";
if (dueDate != null) {
paymentTermsXml += "<ram:DueDateDateTime>";
paymentTermsXml += "<udt:DateTimeString format=\"" + dueDate.getFormat().getDateTimeType() + "\">"
+ dueDate.getFormat().getFormatter().format(dueDate.getDate()) + "</udt:DateTimeString>";
paymentTermsXml += "</ram:DueDateDateTime>";
}
if (discountTerms != null) {
paymentTermsXml += "<ram:ApplicableTradePaymentDiscountTerms>";
String currency = trans.getCurrency();
String basisAmount = currencyFormat(getTotalGross());
paymentTermsXml += "<ram:BasisAmount currencyID=\"" + currency + "\">" + basisAmount + "</ram:BasisAmount>";
paymentTermsXml += "<ram:CalculationPercent>" + discountTerms.getCalculationPercentage().toString()
+ "</ram:CalculationPercent>";
if (discountTerms.getBaseDate() != null) {
Date baseDate = discountTerms.getBaseDate().getDate();
ZUGFeRDDateFormat baseDateFormat = discountTerms.getBaseDate().getFormat();
paymentTermsXml += "<ram:BasisDateTime>";
paymentTermsXml += "<udt:DateTimeString format=\"" + baseDateFormat.getDateTimeType() + "\">" + baseDateFormat.getFormatter().format(baseDate) + "</udt:DateTimeString>";
paymentTermsXml += "</ram:BasisDateTime>";
paymentTermsXml += "<ram:BasisPeriodMeasure unitCode=\"" + discountTerms.getBasePeriodUnitCode() + "\">"
+ discountTerms.getBasePeriodMeasure() + "</ram:BasisPeriodMeasure>";
}
paymentTermsXml += "</ram:ApplicableTradePaymentDiscountTerms>";
}
paymentTermsXml += "</ram:SpecifiedTradePaymentTerms>";
return paymentTermsXml;
}
}

View File

@@ -0,0 +1,413 @@
/**
* *********************************************************************
* <p>
* Copyright 2018 Jochen Staerk
* <p>
* Use is subject to license terms.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* <p>
* See the License for the specific language governing permissions and
* limitations under the License.
* <p>
* **********************************************************************
*/
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
public class ZUGFeRD2PushProvider implements IZUGFeRDExportableTransaction {
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, ownTaxID = null, ownVATID = null, ownForeignOrganisationID = null, ownOrganisationName = null, ownStreet = null, ownZIP = null, ownLocation = null, ownCountry = null, currency = null, paymentTermDescription = null;
protected Date issueDate = null, dueDate = null, deliveryDate = null;
protected BigDecimal totalPrepaidAmount = null;
protected IZUGFeRDExportableContact ownContact = null, recipient = null, deliveryAddress = null;
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
protected IZUGFeRDAllowanceCharge[] ZFAllowances = null, ZFCharges = null, ZFLogisticsServiceCharges = null;
protected IZUGFeRDTradeSettlement[] getTradeSettlement = null;
protected IZUGFeRDPaymentTerms paymentTerms = null;
public ZUGFeRD2PushProvider() {
ZFItems = new ArrayList<IZUGFeRDExportableItem>();
}
@Override
public String getDocumentName() {
return documentName;
}
public ZUGFeRD2PushProvider setDocumentName(String documentName) {
this.documentName = documentName;
return this;
}
@Override
public String getDocumentCode() {
return documentCode;
}
public ZUGFeRD2PushProvider setDocumentCode(String documentCode) {
this.documentCode = documentCode;
return this;
}
@Override
public String getNumber() {
return number;
}
public ZUGFeRD2PushProvider setNumber(String number) {
this.number = number;
return this;
}
@Override
public String getOwnOrganisationFullPlaintextInfo() {
return ownOrganisationFullPlaintextInfo;
}
public ZUGFeRD2PushProvider setOwnOrganisationFullPlaintextInfo(String ownOrganisationFullPlaintextInfo) {
this.ownOrganisationFullPlaintextInfo = ownOrganisationFullPlaintextInfo;
return this;
}
@Override
public String getReferenceNumber() {
return referenceNumber;
}
public ZUGFeRD2PushProvider setReferenceNumber(String referenceNumber) {
this.referenceNumber = referenceNumber;
return this;
}
@Override
public String getShipToOrganisationID() {
return shipToOrganisationID;
}
public ZUGFeRD2PushProvider setShipToOrganisationID(String shipToOrganisationID) {
this.shipToOrganisationID = shipToOrganisationID;
return this;
}
@Override
public String getShipToOrganisationName() {
return shipToOrganisationName;
}
public ZUGFeRD2PushProvider setShipToOrganisationName(String shipToOrganisationName) {
this.shipToOrganisationName = shipToOrganisationName;
return this;
}
@Override
public String getShipToStreet() {
return shipToStreet;
}
public ZUGFeRD2PushProvider setShipToStreet(String shipToStreet) {
this.shipToStreet = shipToStreet;
return this;
}
@Override
public String getShipToZIP() {
return shipToZIP;
}
public ZUGFeRD2PushProvider setShipToZIP(String shipToZIP) {
this.shipToZIP = shipToZIP;
return this;
}
@Override
public String getShipToLocation() {
return shipToLocation;
}
public ZUGFeRD2PushProvider setShipToLocation(String shipToLocation) {
this.shipToLocation = shipToLocation;
return this;
}
@Override
public String getShipToCountry() {
return shipToCountry;
}
public ZUGFeRD2PushProvider setShipToCountry(String shipToCountry) {
this.shipToCountry = shipToCountry;
return this;
}
@Override
public String getBuyerOrderReferencedDocumentID() {
return buyerOrderReferencedDocumentID;
}
public ZUGFeRD2PushProvider setBuyerOrderReferencedDocumentID(String buyerOrderReferencedDocumentID) {
this.buyerOrderReferencedDocumentID = buyerOrderReferencedDocumentID;
return this;
}
@Override
public String getBuyerOrderReferencedDocumentIssueDateTime() {
return buyerOrderReferencedDocumentIssueDateTime;
}
public ZUGFeRD2PushProvider setBuyerOrderReferencedDocumentIssueDateTime(String buyerOrderReferencedDocumentIssueDateTime) {
this.buyerOrderReferencedDocumentIssueDateTime = buyerOrderReferencedDocumentIssueDateTime;
return this;
}
@Override
public String getOwnTaxID() {
return ownTaxID;
}
public ZUGFeRD2PushProvider setOwnTaxID(String ownTaxID) {
this.ownTaxID = ownTaxID;
return this;
}
@Override
public String getOwnVATID() {
return ownVATID;
}
public ZUGFeRD2PushProvider setOwnVATID(String ownVATID) {
this.ownVATID = ownVATID;
return this;
}
@Override
public String getOwnForeignOrganisationID() {
return ownForeignOrganisationID;
}
public ZUGFeRD2PushProvider setOwnForeignOrganisationID(String ownForeignOrganisationID) {
this.ownForeignOrganisationID = ownForeignOrganisationID;
return this;
}
@Override
public String getOwnOrganisationName() {
return ownOrganisationName;
}
public ZUGFeRD2PushProvider setOwnOrganisationName(String ownOrganisationName) {
this.ownOrganisationName = ownOrganisationName;
return this;
}
@Override
public String getOwnStreet() {
return ownStreet;
}
public ZUGFeRD2PushProvider setOwnStreet(String ownStreet) {
this.ownStreet = ownStreet;
return this;
}
@Override
public String getOwnZIP() {
return ownZIP;
}
public ZUGFeRD2PushProvider setOwnZIP(String ownZIP) {
this.ownZIP = ownZIP;
return this;
}
public String getOwnLocation() {
return ownLocation;
}
public ZUGFeRD2PushProvider setOwnLocation(String getOwnLocation) {
this.ownLocation = getOwnLocation;
return this;
}
public String getOwnCountry() {
return ownCountry;
}
public ZUGFeRD2PushProvider setOwnCountry(String getOwnCountry) {
this.ownCountry = getOwnCountry;
return this;
}
@Override
public String getCurrency() {
return currency;
}
public ZUGFeRD2PushProvider setCurrency(String currency) {
this.currency = currency;
return this;
}
@Override
public String getPaymentTermDescription() {
return paymentTermDescription;
}
public ZUGFeRD2PushProvider setPaymentTermDescription(String paymentTermDescription) {
this.paymentTermDescription = paymentTermDescription;
return this;
}
@Override
public Date getIssueDate() {
return issueDate;
}
public ZUGFeRD2PushProvider setIssueDate(Date issueDate) {
this.issueDate = issueDate;
return this;
}
@Override
public Date getDueDate() {
return dueDate;
}
public ZUGFeRD2PushProvider setDueDate(Date dueDate) {
this.dueDate = dueDate;
return this;
}
@Override
public Date getDeliveryDate() {
return deliveryDate;
}
public ZUGFeRD2PushProvider setDeliveryDate(Date deliveryDate) {
this.deliveryDate = deliveryDate;
return this;
}
@Override
public BigDecimal getTotalPrepaidAmount() {
return totalPrepaidAmount;
}
public ZUGFeRD2PushProvider setTotalPrepaidAmount(BigDecimal totalPrepaidAmount) {
this.totalPrepaidAmount = totalPrepaidAmount;
return this;
}
@Override
public IZUGFeRDExportableContact getOwnContact() {
return ownContact;
}
public ZUGFeRD2PushProvider setOwnContact(IZUGFeRDExportableContact ownContact) {
this.ownContact = ownContact;
return this;
}
@Override
public IZUGFeRDExportableContact getRecipient() {
return recipient;
}
public ZUGFeRD2PushProvider setRecipient(IZUGFeRDExportableContact recipient) {
this.recipient = recipient;
return this;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
return ZFAllowances;
}
public ZUGFeRD2PushProvider setZFAllowances(IZUGFeRDAllowanceCharge[] ZFAllowances) {
this.ZFAllowances = ZFAllowances;
return this;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFCharges() {
return ZFCharges;
}
public ZUGFeRD2PushProvider setZFCharges(IZUGFeRDAllowanceCharge[] ZFCharges) {
this.ZFCharges = ZFCharges;
return this;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return ZFLogisticsServiceCharges;
}
public ZUGFeRD2PushProvider setZFLogisticsServiceCharges(IZUGFeRDAllowanceCharge[] ZFLogisticsServiceCharges) {
this.ZFLogisticsServiceCharges = ZFLogisticsServiceCharges;
return this;
}
public IZUGFeRDTradeSettlement[] getGetTradeSettlement() {
return getTradeSettlement;
}
public ZUGFeRD2PushProvider setGetTradeSettlement(IZUGFeRDTradeSettlement[] getTradeSettlement) {
this.getTradeSettlement = getTradeSettlement;
return this;
}
@Override
public IZUGFeRDPaymentTerms getPaymentTerms() {
return paymentTerms;
}
public ZUGFeRD2PushProvider setPaymentTerms(IZUGFeRDPaymentTerms paymentTerms) {
this.paymentTerms = paymentTerms;
return this;
}
@Override
public IZUGFeRDExportableContact getDeliveryAddress() {
return deliveryAddress;
}
public ZUGFeRD2PushProvider setDeliveryAddress(IZUGFeRDExportableContact deliveryAddress) {
this.deliveryAddress = deliveryAddress;
return this;
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
return ZFItems.toArray(new IZUGFeRDExportableItem[0]);
}
public ZUGFeRD2PushProvider addItem(IZUGFeRDExportableItem item) {
ZFItems.add(item);
return this;
}
public boolean isValid() {
return (dueDate != null) && (ownZIP != null) && (ownStreet != null) && (ownLocation != null) && (ownCountry != null) && (ownTaxID != null) && (ownVATID != null) && (recipient != null);
//contact
// this.phone = phone;
// this.email = email;
// this.street = street;
// this.zip = zip;
// this.location = location;
// this.country = country;
}
}

View File

@@ -0,0 +1,23 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public enum ZUGFeRDConformanceLevel {
BASIC, COMFORT, EXTENDED, EN16931/*=Comfort*/, MINIMUM, BASICWL /*basic without lines, less than basic*/, CIUS
}

View File

@@ -0,0 +1,28 @@
package org.mustangproject.ZUGFeRD;
import java.text.SimpleDateFormat;
import org.mustangproject.ZUGFeRD.model.DateTimeTypeConstants;
public enum ZUGFeRDDateFormat {
MONTH_OF_YEAR(DateTimeTypeConstants.MONTH, new SimpleDateFormat("yyyyMM")),
WEEK_OF_YEAR(DateTimeTypeConstants.WEEK, new SimpleDateFormat("yyyyww")),
DATE(DateTimeTypeConstants.DATE, new SimpleDateFormat("yyyyMMdd"));
private String dateTimeType;
private SimpleDateFormat formatter;
private ZUGFeRDDateFormat(String dateTimeType, SimpleDateFormat formatter) {
this.dateTimeType = dateTimeType;
this.formatter = formatter;
}
public String getDateTimeType() {
return dateTimeType;
}
public SimpleDateFormat getFormatter() {
return formatter;
}
}

View File

@@ -0,0 +1,41 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public class ZUGFeRDExportException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public ZUGFeRDExportException() {
}
public ZUGFeRDExportException(String message) {
super(message);
}
public ZUGFeRDExportException(String message, Throwable cause) {
super(message, cause);
}
public ZUGFeRDExportException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,746 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
/*
* Mustangproject's ZUGFeRD implementation ZUGFeRD exporter Licensed under the
* APLv2
*
* @date 2014-07-12
* @version 1.2.0
* @author jstaerk
*
*/
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
public class ZUGFeRDExporter implements Closeable {
public static final int DefaultZUGFeRDVersion = 2;
private boolean isFacturX = false;
/**
* To use the ZUGFeRD exporter, implement IZUGFeRDExportableTransaction in
* yourTransaction (which will require you to implement Product, Item and
* Contact) then call doc = PDDocument.load(PDFfilename); // automatically add
* Zugferd to all outgoing invoices ZUGFeRDExporter ze = new ZUGFeRDExporter();
* ze.PDFmakeA3compliant(doc, "Your application name",
* System.getProperty("user.name"), true); ze.PDFattachZugferdFile(doc,
* yourTransaction);
* <p/>
* doc.save(PDFfilename);
*
* @author jstaerk
* @deprecated Use the factory methods {@link #createFromPDFA3(String)},
* {@link #createFromPDFA3(InputStream)} or the
* {@link ZUGFeRDExporterFromA1Factory} instead
*/
// // MAIN CLASS
@Deprecated
private PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE;
// BASIC, COMFORT etc - may be set from outside.
@Deprecated
private ZUGFeRDConformanceLevel profile = ZUGFeRDConformanceLevel.EN16931;
/**
* Data (XML invoice) to be added to the ZUGFeRD PDF. It may be externally set,
* in which case passing a IZUGFeRDExportableTransaction is not necessary. By
* default it is null meaning the caller needs to pass a
* IZUGFeRDExportableTransaction for the XML to be populated.
*/
IXMLProvider xmlProvider;
protected PDMetadata metadata = null;
protected PDFAIdentificationSchema pdfaid = null;
protected XMPMetadata xmp = null;
/**
* Producer attribute for PDF
*/
protected String producer = "mustangproject";
/**
* Author/Creator attribute for PDF
*/
protected String creator = "mustangproject";
/**
* CreatorTool
*/
protected String creatorTool = "mustangproject";
@Deprecated
private boolean ignoreA1Errors;
protected boolean ensurePDFisUpgraded = true;
private PDDocument doc;
int ZFVersion;
private HashMap<String, byte[]> additionalFiles = new HashMap<String, byte[]>();
private boolean disableAutoClose;
private boolean fileAttached = false;
protected boolean attachZUGFeRDHeaders = true;
private boolean documentPrepared = false;
public ZUGFeRDExporter() {
init();
}
public ZUGFeRDExporter(PDDocument doc2) {
doc = doc2;
init();
}
/**
* Adds additional file attachments into the ZF
* @param filename the name of the attachment
* @param filecontent the bytearray with the file contents
*/
public void addAdditionalFile(String filename, byte[] filecontent) {
additionalFiles.put(filename, filecontent);
}
/***
* internal helper function: get namespace for given zugferd or factur-x version
* @param ver the ZUGFeRD version
* @return the URN of the namespace
*/
public String getNamespaceForVersion(int ver) {
if (isFacturX) {
return "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#";
} else if (ver == 1) {
return "urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#";
} else if (ver == 2) {
return "urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#";
} else {
throw new IllegalArgumentException("Version not supported");
}
}
/***
* internal helper: returns the namespace prefix for the given zf/fx version number
* @param ver the zf/fx version
* @return the namespace prefix as string, without colon
*/
public String getPrefixForVersion(int ver) {
if (isFacturX) {
return "fx";
} else {
return "zf";
}
}
/***
* internal helper: return the name of the file attachment for the given zf/fx version
* @param ver the zf/fx version
* @return the filename of the file to be embedded
*/
public String getFilenameForVersion(int ver) {
if (isFacturX) {
return "factur-x.xml";
} else {
if (ver==1) {
return "ZUGFeRD-invoice.xml";
} else {
return "zugferd-invoice.xml";
}
}
}
@Deprecated
public void setZUGFeRDVersion(int ver) {
if (ver == 1) {
ZUGFeRD1PullProvider z1p = new ZUGFeRD1PullProvider();
this.xmlProvider = z1p;
} else if (ver == 2) {
ZUGFeRD2PullProvider z2p = new ZUGFeRD2PullProvider();
this.xmlProvider = z2p;
} else {
throw new IllegalArgumentException("Version not supported");
}
ZFVersion = ver;
}
public IXMLProvider getProvider() {
return xmlProvider;
}
private void init() {
setZUGFeRDVersion(DefaultZUGFeRDVersion);
}
public void setFacturX() {
setZUGFeRDVersion(2);
isFacturX = true;
}
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
*
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
* @param newLevel "A", "U" or "B"
*
* @deprecated Use {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
public void setConformanceLevel(PDFAConformanceLevel newLevel) {
if (newLevel == null) {
throw new NullPointerException("pdf conformance level");
}
conformanceLevel = newLevel;
}
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
* <p>
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
*
* @param newLevel "A", "U" or "B"
* @deprecated Use
* {@link #setConformanceLevel(org.mustangproject.ZUGFeRD.PDFAConformanceLevel)}
* instead
*/
@Deprecated
public void setConformanceLevel(String newLevel) {
conformanceLevel = PDFAConformanceLevel.findByLetter(newLevel);
}
/**
* enables the flag to indicate a test invoice in the XML structure
*/
public void setTest() {
xmlProvider.setTest();
}
/**
* @deprecated Use {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
public void ignoreA1Errors() {
ignoreA1Errors = true;
}
/***
* load from a pdf which is already A/3
* @param filename the PDF
* @throws IOException if anything is wrong with filename
* @deprecated Use the factory method {@link #createFromPDFA3(String)} instead
*/
@Deprecated
public void loadPDFA3(String filename) throws IOException {
doc = PDDocument.load(new File(filename));
}
/***
* factory create a ZUGFeRD exporter for a PDF/A-3 pdf file
* @param filename of the pdf
* @return the created ZUGFeRDExporter
* @throws IOException if something is wrong with filename
*/
public static ZUGFeRDExporter createFromPDFA3(String filename) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(new File(filename)));
}
/**
* load from a pdf which is already A/3 from filestream
* @param file InputStream to load pdf from
* @deprecated Use the factory method {@link #createFromPDFA3(InputStream)} instead
* @throws IOException in case anything is wrong with file
*
*/
@Deprecated
public void loadPDFA3(InputStream file) throws IOException {
doc = PDDocument.load(file);
}
/***
* factory create a ZUGFeRD exporter for a PDF/A-3 inputstream
* @param pdfSource the InputStream of the pdf
* @return the created ZUGFeRDExporter
* @throws IOException (should not happen)
*/
public static ZUGFeRDExporter createFromPDFA3(InputStream pdfSource) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(pdfSource));
}
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
* @param filename the pdf
* @param producer the name of the xmp producer (app)
* @param creator the name of the xmp creator (user)
* @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
* @return the created PDFbox PDDocumentCatalog
* @throws IOException if anything is wrong with file
* @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
public PDDocumentCatalog PDFmakeA3compliant(String filename, String producer, String creator,
boolean attachZugferdHeaders) throws IOException, TransformerException {
doc = createPDFA1Factory().setProducer(producer).setCreator(creator).load(filename).doc;
return doc.getDocumentCatalog();
}
/***
*
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
* @param file the InputStream of the pdf
* @param producer the name of the xmp producer (app)
* @param creator the name of the xmp creator (user)
* @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
* @return the created PDFbox PDDocumentCatalog
* @throws IOException if anything is wrong with file
* @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
public PDDocumentCatalog PDFmakeA3compliant(InputStream file, String producer, String creator,
boolean attachZugferdHeaders) throws IOException, TransformerException {
doc = createPDFA1Factory().setProducer(producer).setCreator(creator).load(file).doc;
return doc.getDocumentCatalog();
}
private IExporterFactory createPDFA1Factory() {
ZUGFeRDExporterFromA1Factory factory = new ZUGFeRDExporterFromA1Factory();
if (ignoreA1Errors) {
factory.ignorePDFAErrors();
}
return factory.setZUGFeRDConformanceLevel(profile).setConformanceLevel(conformanceLevel);
}
@Override
public void close() throws IOException {
if (doc != null) {
doc.close();
}
}
/**
* Embeds the Zugferd XML structure in a file named ZUGFeRD-invoice.xml.
*
* @param trans a IZUGFeRDExportableTransaction that provides the data-model to
* populate the XML. This parameter may be null, if so the XML data
* should hav ebeen set via
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF
*/
public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) throws IOException {
prepareDocument();
((IProfileProvider) xmlProvider).setProfile(profile);
xmlProvider.generateXML(trans);
String filename = getFilenameForVersion(ZFVersion);
PDFAttachGenericFile(doc, filename, "Alternative",
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
"text/xml", xmlProvider.getXML());
for (String filenameAdditional : additionalFiles.keySet()) {
PDFAttachGenericFile(doc, filenameAdditional, "Supplement", "ZUGFeRD extension/additional data", "text/xml", additionalFiles.get(filenameAdditional));
}
}
/***
* Perform the final export to a now ZUGFeRD-enriched PDF file
* @param ZUGFeRDfilename the pdf file name
* @throws IOException if anything is wrong in the target location
*/
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) {
prepareDocument();
}
if ((!fileAttached) && (attachZUGFeRDHeaders)) {
throw new IOException(
"File must be attached (usually with PDFattachZugferdFile) before perfoming this operation");
}
doc.save(ZUGFeRDfilename);
if (!disableAutoClose) {
close();
}
}
/***
* Perform the final export to a now ZUGFeRD-enriched PDF file as OutputStream
* @param output the OutputStream
* @throws IOException if anything is wrong in the OutputStream
*/
public void export(OutputStream output) throws IOException {
if (!documentPrepared) {
prepareDocument();
}
if ((!fileAttached) && (attachZUGFeRDHeaders)) {
throw new IOException(
"File must be attached (usually with PDFattachZugferdFile) before perfoming this operation");
}
doc.save(output);
if (!disableAutoClose) {
close();
}
}
/**
* Embeds an external file (generic - any type allowed) in the PDF.
*
* @param doc PDDocument to attach the file to.
* @param filename name of the file that will become attachment name in the PDF
* @param relationship how the file relates to the content, e.g. "Alternative"
* @param description Human-readable description of the file content
* @param subType type of the data e.g. could be "text/xml" - mime like
* @param data the binary data of the file/attachment
* @throws java.io.IOException if anything is wrong with filename
*/
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException {
fileAttached = true;
PDComplexFileSpecification fs = new PDComplexFileSpecification();
fs.setFile(filename);
COSDictionary dict = fs.getCOSObject();
dict.setName("AFRelationship", relationship);
dict.setString("UF", filename);
dict.setString("Desc", description);
ByteArrayInputStream fakeFile = new ByteArrayInputStream(data);
PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile);
// ef.addCompression();
ef.setSubtype(subType);
ef.setSize(data.length);
ef.setCreationDate(new GregorianCalendar());
ef.setModDate(GregorianCalendar.getInstance());
fs.setEmbeddedFile(ef);
// In addition make sure the embedded file is set under /UF
dict = fs.getCOSObject();
COSDictionary efDict = (COSDictionary) dict.getDictionaryObject(COSName.EF);
COSBase lowerLevelFile = efDict.getItem(COSName.F);
efDict.setItem(COSName.UF, lowerLevelFile);
// now add the entry to the embedded file tree and set in the document.
PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles();
if (efTree == null) {
efTree = new PDEmbeddedFilesNameTreeNode();
}
Map<String, PDComplexFileSpecification> namesMap = new HashMap<>();
Map<String, PDComplexFileSpecification> oldNamesMap = efTree.getNames();
if (oldNamesMap != null) {
for (String key : oldNamesMap.keySet()) {
namesMap.put(key, oldNamesMap.get(key));
}
}
namesMap.put(filename, fs);
efTree.setNames(namesMap);
names.setEmbeddedFiles(efTree);
doc.getDocumentCatalog().setNames(names);
// AF entry (Array) in catalog with the FileSpec
COSBase AFEntry = (COSBase)doc.getDocumentCatalog().getCOSObject().getItem("AF");
if ((AFEntry == null))
{
COSArray cosArray = new COSArray();
cosArray.add(fs);
doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
} else if (AFEntry instanceof COSArray)
{
COSArray cosArray = (COSArray)AFEntry;
cosArray.add(fs);
doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
} else if ((AFEntry instanceof COSObject) &&
((COSObject)AFEntry).getObject() instanceof COSArray)
{
COSArray cosArray = (COSArray)((COSObject)AFEntry).getObject();
cosArray.add(fs);
} else
{
throw new IOException("Unexpected object type for PDFDocument/Catalog/COSDictionary/Item(AF)");
}
}
/**
* Sets the ZUGFeRD XML data to be attached as a single byte array. This is
* useful for use-cases where the XML has already been produced by some external
* API or component.
*
* @param zugferdData XML data to be set as a byte array (XML file in raw form).
* @throws IOException (should not happen)
*/
public void setZUGFeRDXMLData(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider();
cus.setXML(zugferdData);
this.xmlProvider = cus;
PDFattachZugferdFile(null);
}
/**
* Sets the ZUGFeRD profile.
*
* @param zUGFeRDConformanceLevel the new conformance level
* @deprecated Use {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
public void setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel zUGFeRDConformanceLevel) {
if (zUGFeRDConformanceLevel == null) {
throw new NullPointerException("ZUGFeRD conformance level");
}
this.profile = zUGFeRDConformanceLevel;
}
/**
* Sets the ZUGFeRD profile.
*
* @param zUGFeRDConformanceLevel the new conformance level
* @deprecated Use {@link #setConformanceLevel(PDFAConformanceLevel)} instead
*/
@Deprecated
public void setZUGFeRDConformanceLevel(String zUGFeRDConformanceLevel) {
this.profile = ZUGFeRDConformanceLevel.valueOf(zUGFeRDConformanceLevel);
}
/**
* *
* Returns the PDFBox PDF Document
*
* @return PDDocument the PDFBox PDF
*/
public PDDocument getDoc() {
return doc;
}
/**
* This will add both the RDF-indication which embedded file is Zugferd and the
* neccessary PDF/A schema extension description to be able to add this
* information to RDF
*
* @param metadata the PDFbox XMPMetadata object
*/
protected void addXMP(XMPMetadata metadata) {
if (attachZUGFeRDHeaders) {
XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, ZFVersion, isFacturX, profile,
getNamespaceForVersion(ZFVersion), getPrefixForVersion(ZFVersion),
getFilenameForVersion(ZFVersion));
metadata.addSchema(zf);
}
XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(this, metadata, ZFVersion, attachZUGFeRDHeaders);
pdfaex.setZUGFeRDVersion(ZFVersion);
metadata.addSchema(pdfaex);
}
protected byte[] serializeXmpMetadata(XMPMetadata xmpMetadata) throws TransformerException {
XmpSerializer serializer = new XmpSerializer();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
String prefix = "<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>";
String suffix = "<?xpacket end=\"w\"?>";
try {
buffer.write(prefix.getBytes("UTF-8")); // see https://github.com/ZUGFeRD/mustangproject/issues/44
serializer.serialize(xmpMetadata, buffer, false);
buffer.write(suffix.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e)
{
throw new TransformerException(e);
} catch (IOException e)
{
throw new TransformerException(e);
}
return buffer.toByteArray();
}
protected void prepareDocument() throws IOException {
String fullProducer = producer + " (via mustangproject.org " + org.mustangproject.ZUGFeRD.Version.VERSION + ")";
PDDocumentCatalog cat = doc.getDocumentCatalog();
metadata = new PDMetadata(doc);
cat.setMetadata(metadata);
xmp = XMPMetadata.createXMPMetadata();
pdfaid = new PDFAIdentificationSchema(xmp);
xmp.addSchema(pdfaid);
DublinCoreSchema dc = xmp.createAndAddDublinCoreSchema();
dc.addCreator(creator);
XMPBasicSchema xsb = xmp.createAndAddXMPBasicSchema();
xsb.setCreatorTool(creatorTool);
xsb.setCreateDate(GregorianCalendar.getInstance());
// PDDocumentInformation pdi=doc.getDocumentInformation();
PDDocumentInformation pdi = new PDDocumentInformation();
pdi.setProducer(fullProducer);
pdi.setAuthor(creator);
doc.setDocumentInformation(pdi);
AdobePDFSchema pdf = xmp.createAndAddAdobePDFSchema();
pdf.setProducer(fullProducer);
if (ensurePDFisUpgraded) {
try {
pdfaid.setConformance(conformanceLevel.getLetter());// $NON-NLS-1$ //$NON-NLS-1$
} catch (BadFieldValueException ex) {
// This should be impossible, because it would occur only if an illegal
// conformance level is supplied,
// however the enum enforces that the conformance level is valid.
throw new Error(ex);
}
pdfaid.setPart(3);
}
addXMP(xmp); /*
* this is the only line where we do something Zugferd-specific, i.e. add PDF
* metadata specifically for Zugferd, not generically for a embedded file
*/
try {
metadata.importXMPMetadata(serializeXmpMetadata(xmp));
} catch (TransformerException e) {
throw new ZUGFeRDExportException("Could not export XmpMetadata", e);
}
documentPrepared = true;
}
/**
* @return if pdf file will be automatically closed after adding ZF
*/
public boolean isAutoCloseDisabled() {
return disableAutoClose;
}
/**
* @param disableAutoClose prevent PDF file from being closed after adding ZF
*/
public void disableAutoClose(boolean disableAutoClose) {
this.disableAutoClose = disableAutoClose;
}
/**
* the human author (use factory method instead)
*
* @param creator the (human) name who created the PDF
*/
@Deprecated
public void setCreator(String creator) {
this.creator = creator;
}
/**
* the CreatorTool attribute for the PDF
*
* @param creatorTool the application which created the PDF
*/
protected void setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool;
}
/**
* the authoring software (use factory method instead)
*
* @param producer the authoring software
*/
@Deprecated
public void setProducer(String producer) {
this.producer = producer;
}
/**
* @param ensurePDFisUpgraded if not set the PDF/A won't be relabelled A/3, e.g. if it already is one
*/
public void setPDFA3(boolean ensurePDFisUpgraded) {
this.ensurePDFisUpgraded = ensurePDFisUpgraded;
}
/**
* @param attachZUGFeRDHeaders if false the ZUGFeRD XMP metadata won't be added, e.g. if it's not the first file
*/
public void setAttachZUGFeRDHeaders(boolean attachZUGFeRDHeaders) {
this.attachZUGFeRDHeaders = attachZUGFeRDHeaders;
}
/**
* encapsulate the deprecated setters
* @param zfVersion 1 or 2
* @param zugferdConformanceLevel BASIC, COMFORT, EN16931, etc.
* @param creator PDF creator
* @param producer PDF producer
*/
public void configure(int zfVersion, ZUGFeRDConformanceLevel zugferdConformanceLevel, String creator, String producer) {
setZUGFeRDVersion(zfVersion);
setZUGFeRDConformanceLevel(zugferdConformanceLevel);
setCreator(creator);
setProducer(producer);
}
}

View File

@@ -0,0 +1,28 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public class ZUGFeRDExporterFromA1Factory extends ZUGFeRDExporterFromA3Factory implements IExporterFactory {
public ZUGFeRDExporterFromA1Factory() {
ensurePDFisUpgraded = true;
}
}

View File

@@ -0,0 +1,208 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import java.io.*;
import java.util.HashMap;
public class ZUGFeRDExporterFromA3Factory implements IExporterFactory {
protected boolean ignorePDFAErrors = false;
protected ZUGFeRDConformanceLevel zugferdConformanceLevel = ZUGFeRDConformanceLevel.EXTENDED;
protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE;
/**
* Producer (attribute for PDF
*/
protected String producer = "mustangproject";
/**
* Human creator (attribute for PDF)
*/
protected String creator = "mustangproject";
/**
* Creator tool (attribute for PDF)
*/
protected String creatorTool = null;
private HashMap<String, byte[]> additionalXMLs = new HashMap<String, byte[]>();
protected int ZFVersion = ZUGFeRDExporter.DefaultZUGFeRDVersion;
protected boolean ensurePDFisUpgraded = false;
private boolean attachZUGFeRDHeaders = true;
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfFilename filename of an PDF/A1 compliant document
*/
public ZUGFeRDExporter load(String pdfFilename) throws IOException {
ensurePDFIsValidPDFA(new FileDataSource(pdfFilename));
try (FileInputStream pdf = new FileInputStream(pdfFilename)) {
return load(readAllBytes(pdf));
}
}
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfBinary binary of a PDF/A1 compliant document
*/
public ZUGFeRDExporter load(byte[] pdfBinary) throws IOException {
ensurePDFIsValidPDFA(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary)));
PDDocument doc = PDDocument.load(pdfBinary);
ZUGFeRDExporter zugFeRDExporter = new ZUGFeRDExporter(doc);
zugFeRDExporter.configure(ZFVersion, zugferdConformanceLevel, creator, producer);
zugFeRDExporter.setAttachZUGFeRDHeaders(attachZUGFeRDHeaders);
zugFeRDExporter.setPDFA3(ensurePDFisUpgraded);
return zugFeRDExporter;
}
public ZUGFeRDExporterFromA3Factory() {
ensurePDFisUpgraded = false;
}
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfSource source to read a PDF/A1 compliant document from
*/
public ZUGFeRDExporter load(InputStream pdfSource) throws IOException {
return load(readAllBytes(pdfSource));
}
private void ensurePDFIsValidPDFA(final DataSource dataSource) throws IOException {
if (!ignorePDFAErrors && !isValidA1(dataSource)) {
throw new IOException("File is not a valid PDF/A input file");
}
}
private static byte[] readAllBytes(InputStream in) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
IOUtils.copy(in, buffer);
return buffer.toByteArray();
}
private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(new PreflightParser(dataSource));
}
/**
* Sets the ZUGFeRD conformance level (override).
*
* @param zugferdConformanceLevel the new conformance level
*/
public IExporterFactory setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel zugferdConformanceLevel) {
this.zugferdConformanceLevel = zugferdConformanceLevel;
return this;
}
private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException {
/*
* Parse the PDF file with PreflightParser that inherits from the
* NonSequentialParser. Some additional controls are present to check a set of
* PDF/A requirements. (Stream length consistency, EOL after some Keyword...)
*/
parser.parse();// might add a Format.PDF_A1A as parameter and iterate through A1 and A3
try (PreflightDocument document = parser.getPreflightDocument()) {
/*
* Once the syntax validation is done, the parser can provide a
* PreflightDocument (that inherits from PDDocument) This document process the
* end of PDF/A validation.
*/
document.validate();
// Get validation result
return document.getResult().isValid();
} catch (ValidationException e) {
/*
* the parse method can throw a SyntaxValidationException if the PDF file can't
* be parsed. In this case, the exception contains an instance of
* ValidationResult
*/
return false;
}
}
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
* <p>
* PDF/A-3 has three coformance levels, called "A", "U" and "B".
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
*/
public IExporterFactory setConformanceLevel(PDFAConformanceLevel newLevel) {
conformanceLevel = newLevel;
return this;
}
public IExporterFactory ignorePDFAErrors() {
this.ignorePDFAErrors = true;
return this;
}
public IExporterFactory setCreator(String creator) {
this.creator = creator;
return this;
}
public IExporterFactory setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool;
return this;
}
public IExporterFactory setProducer(String producer) {
this.producer = producer;
return this;
}
public IExporterFactory setAttachZUGFeRDHeaders(boolean attachHeaders) {
this.attachZUGFeRDHeaders = attachHeaders;
return this;
}
@Override
public IExporterFactory setZUGFeRDVersion(int version) {
this.ZFVersion = version;
return this;
}
}

View File

@@ -0,0 +1,452 @@
/**
* ********************************************************************** Copyright 2018 Jochen Staerk Use is subject to license terms. Licensed under the
* Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation ZUGFeRD importer Licensed under the APLv2
*
* @date 2014-07-07
* @version 1.1.0
* @author jstaerk
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ZUGFeRDImporter {
/**
* if metadata has been found
*/
private boolean containsMeta = false;
/**
* map filenames of additional XML files to their contents
*/
private HashMap<String, byte[]> additionalXMLs = new HashMap<>();
/**
* Raw XML form of the extracted data - may be directly obtained.
*/
private byte[] rawXML = null;
/**
* XMP metadata
*/
private String xmpString = null; // XMP metadata
/**
* parsed Document
*/
private Document document;
public ZUGFeRDImporter(String pdfFilename) {
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
extractLowLevel(bis);
} catch (IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
public ZUGFeRDImporter(InputStream pdfStream) {
try {
extractLowLevel(pdfStream);
} catch (IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
/**
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
*
* @param pdfStream a inputstream of a pdf file
*/
private void extractLowLevel(InputStream pdfStream) throws IOException {
try (PDDocument doc = PDDocument.load(pdfStream)) {
// PDDocumentInformation info = doc.getDocumentInformation();
PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
//start
if (doc.getDocumentCatalog() == null || doc.getDocumentCatalog().getMetadata() == null) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.INFO, "no-xmlpart");
return;
}
InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata();
xmpString = convertStreamToString(XMP);
PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles();
if (etn == null) {
return;
}
Map<String, PDComplexFileSpecification> efMap = etn.getNames();
// String filePath = "/tmp/";
if (efMap != null) {
extractFiles(efMap); // see
// https://memorynotfound.com/apache-pdfbox-extract-embedded-file-pdf-document/
} else {
List<PDNameTreeNode<PDComplexFileSpecification>> kids = etn.getKids();
for (PDNameTreeNode<PDComplexFileSpecification> node : kids) {
Map<String, PDComplexFileSpecification> namesL = node.getNames();
extractFiles(namesL);
}
}
}
}
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException {
for (String alias : names.keySet()) {
PDComplexFileSpecification fileSpec = names.get(alias);
String filename = fileSpec.getFilename();
/**
* filenames for invoice data (ZUGFeRD v1 and v2, Factur-X)
*/
if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml"))) { //$NON-NLS-1$
containsMeta = true;
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
// String embeddedFilename = filePath + filename;
// File file = new File(filePath + filename);
// System.out.println("Writing " + embeddedFilename);
// ByteArrayOutputStream fileBytes=new
// ByteArrayOutputStream();
// FileOutputStream fos = new FileOutputStream(file);
setRawXML(embeddedFile.toByteArray());
// fos.write(embeddedFile.getByteArray());
// fos.close();
}
if (filename.startsWith("additional_data")) {
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
additionalXMLs.put(filename, embeddedFile.toByteArray());
}
}
}
protected Document getDocument() {
return document;
}
private void setDocument() throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
xmlFact.setNamespaceAware(false);
DocumentBuilder builder = xmlFact.newDocumentBuilder();
ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
is.skip(guessBOMSize(is));
document = builder.parse(is);
}
public void setRawXML(byte[] rawXML) throws IOException {
this.rawXML = rawXML;
try {
setDocument();
} catch (ParserConfigurationException | SAXException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
/**
* Skips over a BOM at the beginning of the given ByteArrayInputStream, if one exists.
*
* @param is the ByteArrayInputStream used
* @throws IOException if can not be read from is
* @see <a href="https://www.w3.org/TR/xml/#sec-guessing">Autodetection of Character Encodings</a>
*/
private int guessBOMSize(ByteArrayInputStream is) throws IOException {
byte[] pad = new byte[4];
is.read(pad);
is.reset();
int test2 = ((pad[0] & 0xFF) << 8) | (pad[1] & 0xFF);
int test3 = ((test2 & 0xFFFF) << 8) | (pad[2] & 0xFF);
int test4 = ((test3 & 0xFFFFFF) << 8) | (pad[3] & 0xFF);
//
if (test4 == 0x0000FEFF || test4 == 0xFFFE0000 || test4 == 0x0000FFFE || test4 == 0xFEFF0000) {
// UCS-4: BOM takes 4 bytes
return 4;
} else if (test3 == 0xEFBBFF) {
// UTF-8: BOM takes 3 bytes
return 3;
} else if (test2 == 0xFEFF || test2 == 0xFFFE) {
// UTF-16: BOM takes 2 bytes
return 2;
}
return 0;
}
protected String extractString(String xpathStr) {
if (!containsMeta) {
throw new ZUGFeRDExportException("No suitable data/ZUGFeRD file could be found.");
}
String result;
try {
Document document = getDocument();
XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath();
result = xpath.evaluate(xpathStr, document);
} catch (XPathExpressionException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
return result;
}
/**
* @return the reference (purpose) the sender specified for this invoice
*/
public String getForeignReference() {
String result = extractString("//ApplicableHeaderTradeSettlement/PaymentReference");
if (result == null || result.isEmpty()) {
result = extractString("//ApplicableSupplyChainTradeSettlement/PaymentReference");
}
return result;
}
/**
* @return the document code
*/
public String getDocumentCode() {
return extractString("//HeaderExchangedDocument/TypeCode");
}
/**
* @return the referred document
*/
public String getReference() {
return extractString("//ApplicableHeaderTradeAgreement/BuyerReference");
}
/**
* @return the sender's bank's BLZ code
* @deprecated use BIC and IBAN instead of BLZ and KTO
*/
@Deprecated
public String getBLZ() {
return extractString("//PayeeSpecifiedCreditorFinancialInstitution/GermanBankleitzahlID");
}
/**
* @return the sender's account number
* @deprecated use BIC and IBAN instead of BLZ and KTO
*/
@Deprecated
public String getKTO() {
return extractString("//PayeePartyCreditorFinancialAccount/ProprietaryID");
}
/**
* @return the sender's bank's BIC code
*/
public String getBIC() {
return extractString("//PayeeSpecifiedCreditorFinancialInstitution/BICID");
}
/**
* @return the sender's bank name
*/
public String getBankName() {
return extractString("//PayeeSpecifiedCreditorFinancialInstitution/Name");
}
/**
* @return the sender's account IBAN code
*/
public String getIBAN() {
return extractString("//PayeePartyCreditorFinancialAccount/IBANID");
}
public String getHolder() {
return extractString("//SellerTradeParty/Name");
}
/**
* @return the total payable amount
*/
public String getAmount() {
String result = extractString("//SpecifiedTradeSettlementHeaderMonetarySummation/DuePayableAmount");
if (result == null || result.isEmpty()) {
result = extractString("//SpecifiedTradeSettlementMonetarySummation/GrandTotalAmount");
}
return result;
}
/**
* @return when the payment is due
*/
public String getDueDate() {
return extractString("//SpecifiedTradePaymentTerms/DueDateDateTime/DateTimeString");
}
public HashMap<String, byte[]> getAdditionalData() {
return additionalXMLs;
}
/**
* get xmp metadata of the PDF, null if not available
*
* @return string
*/
public String getXMP() {
return xmpString;
}
/**
* @return if export found parseable ZUGFeRD data
*/
public boolean containsMeta() {
return containsMeta;
}
/**
* @param meta raw XML to be set
* @throws IOException if raw can not be set
*/
public void setMeta(String meta) throws IOException {
setRawXML(meta.getBytes());
}
/**
* @return raw XML of the invoice
*/
public String getMeta() {
if (rawXML == null) {
return null;
}
return new String(rawXML);
}
public int getVersion() throws Exception {
if (!containsMeta) {
throw new Exception("Not yet parsed");
}
if (getUTF8().contains("<rsm:CrossIndustryDocument")) {
return 1;
} else if (getUTF8().contains("<rsm:CrossIndustryInvoice")) {
return 2;
}
throw new Exception("ZUGFeRD version could not be determined");
}
/**
* @return return UTF8 XML (without BOM) of the invoice
*/
public String getUTF8() {
if (rawXML == null) {
return null;
}
if (rawXML.length < 3) {
return new String(rawXML);
}
byte[] bomlessData;
if ((rawXML[0] == (byte) 0xEF)
&& (rawXML[1] == (byte) 0xBB)
&& (rawXML[2] == (byte) 0xBF)) {
// I don't like BOMs, lets remove it
bomlessData = new byte[rawXML.length - 3];
System.arraycopy(rawXML, 3, bomlessData, 0,
rawXML.length - 3);
} else {
bomlessData = rawXML;
}
return new String(bomlessData);
}
/**
* Returns the raw XML data as extracted from the ZUGFeRD PDF file.
*
* @return the raw ZUGFeRD XML data
*/
public byte[] getRawXML() {
return rawXML;
}
/**
* will return true if the metadata (just extract-ed or set with setMeta) contains ZUGFeRD XML
*
* @return true if the invoice contains ZUGFeRD XML
*/
public boolean canParse() {
// SpecifiedExchangedDocumentContext is in the schema, so a relatively good
// indication if zugferd is present - better than just invoice
String meta = getMeta();
return (meta != null) && (meta.length() > 0) && ((meta.contains("SpecifiedExchangedDocumentContext") //$NON-NLS-1$
/* ZF1 */ || meta.contains("ExchangedDocumentContext") /* ZF2 */));
}
static String convertStreamToString(java.io.InputStream is) {
// source https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java referring to
// https://community.oracle.com/blogs/pat/2004/10/23/stupid-scanner-tricks
Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}

View File

@@ -0,0 +1,23 @@
/** **********************************************************************
*
* Copyright 2019 ak on 09.04.19.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public class ZUGFeRDImporterException extends RuntimeException {
}

View File

@@ -0,0 +1,71 @@
package org.mustangproject.ZUGFeRD;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.xpath.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
public ZUGFeRDInvoiceImporter(String filename) {
super(filename);
}
public ZUGFeRD2PushProvider extractInvoice() {
String number="AB123";
ZUGFeRD2PushProvider zpp=new ZUGFeRD2PushProvider().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setOwnStreet("teststr").setOwnZIP("55232").setOwnLocation("teststadt").setOwnCountry("DE").setOwnTaxID("4711").setOwnVATID("0815").setRecipient(new Contact("Franz Müller", "0177123456", "fmueller@test.com", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number);
//.addItem(new Item(new Product("Testprodukt","","C62",new BigDecimal(0)),amount,new BigDecimal(1.0)))
zpp.setOwnOrganisationName(extractString("//SellerTradeParty/Name"));
XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath();
try {
XPathExpression xpr = xpath.compile(
"//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]");
NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (nodes.getLength() == 0) {
} else {
for (int i = 0; i < nodes.getLength(); i++) {
//nodes.item(i).getTextContent())) {
Node currentItemNode=nodes.item(i);
NodeList itemChilds=currentItemNode.getChildNodes();
String price="0";
for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) {
if (itemChilds.item(itemChildIndex).getNodeName().equals("ram:SpecifiedLineTradeAgreement")) {
NodeList tradeLineChilds = itemChilds.item(itemChildIndex).getChildNodes();
for (int tradeLineChildIndex = 0; tradeLineChildIndex < tradeLineChilds.getLength(); tradeLineChildIndex++) {
if (tradeLineChilds.item(tradeLineChildIndex).getNodeName().equals("ram:NetPriceProductTradePrice")) {
NodeList netChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes();
for (int netIndex = 0; netIndex < netChilds.getLength(); netIndex++) {
if (netChilds.item(netIndex).getNodeName().equals("ram:ChargeAmount")) {
price = netChilds.item(netIndex).getTextContent();//ram:ChargeAmount
}
}
}
}
}
}
// Logger.getLogger(ZUGFeRDInvoiceImporter.class.getName()).log(Level.INFO, "deb "+price);
zpp.addItem(new Item(new Product("Testprodukt","","C62",new BigDecimal(0)),new BigDecimal(price),new BigDecimal(1.0)));
}
}
} catch (XPathExpressionException e) {
e.printStackTrace();
}
return zpp;
}
}

View File

@@ -0,0 +1,101 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ZUGFeRDMigrator {
static final ClassLoader CLASS_LOADER = ZUGFeRDMigrator.class.getClassLoader();
private static final String RESOURCE_PATH = ""; //$NON-NLS-1$
private static final Logger LOG = Logger.getLogger(ZUGFeRDMigrator.class.getName());
// private static File createTempFileResult(final Transformer transformer, final StreamSource toTransform,
// final String suffix) throws TransformerException, IOException {
// File result = File.createTempFile("ZUV_", suffix); //$NON-NLS-1$
// result.deleteOnExit();
//
// try (FileOutputStream fos = new FileOutputStream(result)) {
// transformer.transform(toTransform, new StreamResult(fos));
// }
// return result;
// }
private TransformerFactory mFactory = null;
private Templates mXsltTemplate = null;
public ZUGFeRDMigrator() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
//fact = TransformerFactory.newInstance();
mFactory.setURIResolver(new ClasspathResourceURIResolver());
try {
mXsltTemplate = mFactory.newTemplates(new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "COMFORTtoEN16931.xsl")));
} catch (TransformerConfigurationException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
public String migrateFromV1ToV2(String xmlFilename) throws FileNotFoundException, TransformerException, UnsupportedEncodingException {
/**
* *
* 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
*/
ByteArrayOutputStream baos = new ByteArrayOutputStream();
applySchematronXsl(new FileInputStream(xmlFilename), baos);
String res = null;
res = baos.toString("UTF-8");
//migrate the profiles
res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:basic", "urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic");
res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:comfort", "urn:cen.eu:en16931:2017");
res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:extended", "urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended");
//somehow the XML parser seems to insert erreneous XML namespaces, depending on which one it is, saxon seems to be cleaner.
//nevertheless, remove them manually
res=res.replace("<rsm:ExchangedDocument xmlns:qdt=\"urn:un:unece:uncefact:data:standard:QualifiedDataType:100\">", "<rsm:ExchangedDocument>");
res=res.replace("<rsm:SupplyChainTradeTransaction xmlns:qdt=\"urn:un:unece:uncefact:data:standard:QualifiedDataType:100\">", "<rsm:SupplyChainTradeTransaction>");
return res;
}
public void applySchematronXsl(final InputStream xmlFile,
final OutputStream EN16931Outstream) throws TransformerException {
Transformer transformer = mXsltTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(EN16931Outstream));
}
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 + href));
}
}
}

View File

@@ -0,0 +1,165 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ZUGFeRDVisualizer {
static final ClassLoader CLASS_LOADER = ZUGFeRDVisualizer.class.getClassLoader();
private static final String RESOURCE_PATH = ""; //$NON-NLS-1$
private static final Logger LOG = Logger.getLogger(ZUGFeRDVisualizer.class.getName());
// private static File createTempFileResult(final Transformer transformer, final
// StreamSource toTransform,
// final String suffix) throws TransformerException, IOException {
// File result = File.createTempFile("ZUV_", suffix); //$NON-NLS-1$
// result.deleteOnExit();
//
// try (FileOutputStream fos = new FileOutputStream(result)) {
// transformer.transform(toTransform, new StreamResult(fos));
// }
// return result;
// }
private TransformerFactory mFactory = null;
private Templates mXsltXRTemplate = null;
private Templates mXsltHTMLTemplate = null;
private Templates mXsltZF1HTMLTemplate = null;
public ZUGFeRDVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory.setURIResolver(new ClasspathResourceURIResolver());
try {
mXsltXRTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html.xsl")));
mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt")));
} catch (TransformerConfigurationException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
public String visualize(String xmlFilename)
throws FileNotFoundException, TransformerException, UnsupportedEncodingException {
/**
* *
* 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)));
} catch (IOException e2) {
LOG.log(Level.SEVERE, null, e2);
}
ByteArrayOutputStream iaos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String zf1Signature = "rsm:CrossIndustryDocument";
if (fileContent.contains(zf1Signature)) {
applyZF1SchematronXsl(fis, baos);
} else {
//zf2 or fx
applySchematronXsl(fis, iaos);
// 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) {
LOG.log(Level.SEVERE, null, e);
} finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
if (out != null) {
// close the PipedOutputStream cleanly
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE, null, e);
}
}
}
}
}).start();
applySchematronXsl2(in, baos);
} catch (IOException e1) {
LOG.log(Level.SEVERE, null, e1);
}
}
return baos.toString("UTF-8");
}
public void applySchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltXRTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
public void applyZF1SchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltZF1HTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
public void applySchematronXsl2(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltHTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
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 + href));
}
}
}

View File

@@ -0,0 +1,25 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class DateTimeTypeConstants {
public static final String DATE = "102";
public static final String MONTH = "610";
public static final String WEEK = "616";
}

View File

@@ -0,0 +1,25 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class DocumentCodeTypeConstants {
public static final String INVOICE = "380";
public static final String DEBITNOTE = "84";
public static final String CREDITNOTE = "389";
}

View File

@@ -0,0 +1,25 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class DocumentContextParameterTypeConstants {
public static final String BASIC = "urn:ferd:CrossIndustryDocument:invoice:1p0:basic";
public static final String COMFORT = "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort";
public static final String EXTENDED = "urn:ferd:CrossIndustryDocument:invoice:1p0:extended";
}

View File

@@ -0,0 +1,27 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class NoteTypeConstants {
public static final String GENERAL = "";
public static final String REGULARINFO = "REG";
public static final String PRICECONDITION = "AAK";
public static final String CONDITIONS = "AAJ";
public static final String PAYMENTINFO = "PMT";
}

View File

@@ -0,0 +1,31 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class PaymentMeansCodeTypeConstants {
public static final String BANKACCOUNT = "42";
public static final String NOTSPECIFIED = "1";
public static final String AUTOMATICCLEARING = "3";
public static final String CASH = "10";
public static final String CHECK = "20";
public static final String DEBITADVICE = "31";
public static final String CREDITCARD = "48";
public static final String DEBIT = "49";
public static final String COMPENSATION = "97";
}

View File

@@ -0,0 +1,43 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class QuantityTypeConstants {
public static final String PIECE = "C62";
public static final String DAY = "DAY";
public static final String HECTARE = "HAR";
public static final String HOUR = "HUR";
public static final String KILOGRAM = "KGM";
public static final String KILOMETER = "KTM";
public static final String KILOWATTHOUR = "KWH";
public static final String FIXEDRATE = "LS";
public static final String LITRE = "LTR";
public static final String MINUTE = "MIN";
public static final String SQUAREMILLIMETER = "MMK";
public static final String MILLIMETER = "MMT";
public static final String SQUAREMETER = "MTK";
public static final String CUBICMETER = "MTQ";
public static final String METER = "MTR";
public static final String PRODUCTCOUNT = "NAR";
public static final String PRODUCTPAIR = "NPR";
public static final String PERCENT = "P1";
public static final String SET = "SET";
public static final String TON = "TNE";
public static final String WEEK = "WEE";
}

View File

@@ -0,0 +1,28 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class TaxCategoryCodeTypeConstants {
public static final String STANDARDRATE = "S";
public static final String REVERSECHARGE = "AE";
public static final String TAXEXEMPT = "E";
public static final String ZEROTAXPRODUCTS = "Z";
public static final String UNTAXEDSERVICE = "O";
public static final String INTRACOMMUNITY = "IC";
}

View File

@@ -0,0 +1,24 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class TaxRegistrationTypeConstants {
public static final String USTID = "VA";
public static final String TAXID = "FC";
}

View File

@@ -0,0 +1,25 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
public class TaxTypeCodeTypeConstants {
public static final String SALESTAX = "VAT";
public static final String INSURANCETAX = "ZF_INSURANCE_TAX";
public static final String OLDPART = "AAJ";
}

View File

@@ -0,0 +1,49 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD.model;
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
import java.util.HashMap;
import java.util.Map;
public class ZFNamespacePrefixMapper extends NamespacePrefixMapper {
private Map<String, String> namespaceMap = new HashMap<>();
/**
* Create mappings.
*/
public ZFNamespacePrefixMapper() {
namespaceMap.put("urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:12", "ram");
namespaceMap.put("urn:un:unece:uncefact:data:standard:UnqualifiedDataType:15", "udt");
namespaceMap.put("urn:ferd:CrossIndustryDocument:invoice:1p0", "rsm");
}
/* (non-Javadoc)
* Returning null when not found based on spec.
* @see com.sun.xml.bind.marshaller.NamespacePrefixMapper#getPreferredPrefix(java.lang.String, java.lang.String, boolean)
*/
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return namespaceMap.getOrDefault(namespaceUri, suggestion);
}
}