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

This commit is contained in:
Ulrich Zielhofer
2020-11-10 11:25:21 +01:00
14 changed files with 346 additions and 111 deletions

View File

@@ -38,6 +38,8 @@ switch
- new tradeparty class, Contact getOwnContact superseded by TradeParty getSender
- new invoicecorrection class
- order-x xml read support
- support included notes on document and item level
- occurence periods setOccurrencePeriod(Date start, Date end)
- automated tests zuv/verapdf validate created library test files
- trans.getTradeSettlementPayment() removed in favor of trans.getTradeSettlement()
- commandline option for no notices
@@ -58,12 +60,6 @@ switch
- *validator not to XR error on ZF files (only notices)
- *from A3 does not seem tow ork see mustangreaderwriteredgetest:testedgeexport
- xmp errors may not show correctly in log
- confirm correct generation from pdf/a3 files zf2edgetest
- visualization tests
- migration tests
- migration documentation
- *correctiontest exports zf1+zf2?
- *USt 0% wird aus invoice-klasse noch nicht sauber abgefangen
- finalize invoiceimporter
Alpha3 2020-10-24
Alpha2 2020-09-15
@@ -106,6 +102,10 @@ Validator 0.9.0
- prevent accidental CEN EN16931 application on extended profile
- solved issues when XML structures start with comments instead of root node
- added Symtrax signature
- paymenttermsdescription skonto xrechnung ?
- occurencedate vs deliverydate
- exception on invoice import if wrong
Mustangproject 1.7.6
=====

View File

@@ -3,11 +3,21 @@
On it's official website you can [download](https://www.mustangproject.org/files/Mustang-CLI-2.0.0-alpha3.jar) a alpha release of Mustang 2.
It integrates the successor of the ZUGFeRD [Validator ZUV](https://github.com/ZUGFeRD/ZUV/) in it's command line tool and can be used as library.
* Factur-X/ZUGFeRD 2 is now the default. In Mustangproject 1.x one had to select ZUGFeRD version 2 if that was desired, in Mustangproject 2 one now has to select ZUGFeRD version 1 if version 2 is not appropriate.
* Mustang is now available via Maven Central, which makes it even easier to use the Mustang library. Apart from making it easier in Maven because there is no longer the need to mention the custom repository, it also makes it possible to use Mustang in Gradle projects
* ZUGFeRD 2.1.1 is now supported with it's XRechnung "reference profile". The previous stable version, Mustang 1.7.8, only supported ZUGFeRD 2.1.
* There is now an experimental visualization feature and an improved experimental upgrade functionality from ZF1 XML to ZF2 XML.
* Mustang now supports percentual and absolute charges and allowances on both item and document level
* Mustang can now embed additional invoice-accompanying files like plans or worklists into the PDFs
* Additionally to the usual interface-based architecture there are now also classes for invoice, tradeparty etc., which makes it easier to develop in the Mustang library. Please feel free to have a look at it's tests on https://github.com/ZUGFeRD/mustangproject/blob/master/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java.
* Mustang now includes a validator, which can check the syntactical correctness of an e-invoice.
The validator component has previously been developed under the name ZUGFeRD and VeraPDF "ZUV".
It is now mature enought to be merged into the Mustang mainstream project.
ZUV already was the validator behind https://www.zugferd-community.net/de/open_community/validation with around 1,000 validations/month alone,
supports ZUGFeRD 1 and ZUGFeRD 2 and can be used via commandline.
Additionally, the Mustang validator now also supports XRechnung 2, can now also be used as library and supports validation of whole directory trees.
Additionally, the default changed from ZUGFeRD 1 to ZUGFeRD 2.1.1 (previously you had to enable that, now you have to specify that you want to use ZF1 if that's the case), it's now available via Maven Central and additionally to the old Interface-style pullProvider requiring you to implement methods there is now also a "normal", class-orientierted, halfway fluent "Push-Provider". It's describe below, if you are impatient please feel free to have a look at it's tests on https://github.com/ZUGFeRD/mustangproject/blob/master/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java.
This is a preview release, please do not (yet) use it in production.
### Use on command line
`java -jar Mustang-CLI-2.0.0-alpha3.jar --action=combine` embedds a XML into a PDF (A-1) file and exports as PDF/A-3
@@ -68,10 +78,14 @@ as artifact ID. "validator" includes the library functionality but is >20 MB
bigger due to it's dependencies.
### Update from 1.x to 2.0
### Update from Mustang 1.x to 2.0
ZF2 was possible with Mustang 1 but it is default in Mustang 2, so
you will need to `.setZUGFeRDVersion(1)` if you don't want ZUGFeRD 2 files.
In the commandline, all actions will have to be invoked via --action=<theaction>, so
--combine changes to --action=combine.
`PDFattachZugferdFile` is now called `setTransaction` and instead of
a `ZUGFeRDExporterFromA1Factory` the `ZUGFeRDExporterFromA1` will now return a
a class implementing `IZUGFeRDExporter` instead of a `ZUGFeRDExporter`.
@@ -86,6 +100,10 @@ changes to
```
Instead of `Profile.EXTENDED` use `Profiles.getByName("Extended")`.
If you want to use Profiles from older versions, please specify the version like
`Profiles.getByName("Extended")` for an Extended profile of ZF1.
The old Contact class has been corrected to TradeParty. The TradeParty class
can now refer to a (human) Contact from the new Contact() class.

View File

@@ -27,6 +27,10 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
/***
* An invoice, with fluent setters
* @see IExportableTransaction if you want to implement an interface instead
*/
public class Invoice implements IExportableTransaction {
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
@@ -34,6 +38,7 @@ public class Invoice implements IExportableTransaction {
protected BigDecimal totalPrepaidAmount = null;
protected TradeParty sender=null, recipient = null, deliveryAddress = null;
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
protected ArrayList<String> notes = null;
protected String contractReferencedDocument = null;
protected Date occurrenceDateFrom = null;
@@ -266,6 +271,14 @@ public class Invoice implements IExportableTransaction {
}
@Override
public String[] getNotes() {
if (notes==null) {
return null;
}
return notes.toArray(new String[0]);
}
@Override
public String getCurrency() {
return currency;
@@ -492,4 +505,13 @@ public class Invoice implements IExportableTransaction {
}
}
public Invoice addNote(String text) {
if (notes==null) {
notes=new ArrayList<String>();
}
notes.add(text);
return this;
}
}

View File

@@ -10,6 +10,7 @@ public class Item implements IZUGFeRDExportableItem {
protected BigDecimal price, quantity, tax, grossPrice, lineTotalAmount;
protected String id;
protected Product product;
protected ArrayList<String> notes = null;
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<IZUGFeRDAllowanceCharge>(),
Charges = new ArrayList<IZUGFeRDAllowanceCharge>();
@@ -98,6 +99,16 @@ public class Item implements IZUGFeRDExportableItem {
return Charges.toArray(new IZUGFeRDAllowanceCharge[0]);
}
@Override
public String[] getNotes() {
if (notes==null) {
return null;
}
return notes.toArray(new String[0]);
}
public Item setProduct(Product product) {
this.product = product;
return this;
@@ -108,8 +119,19 @@ public class Item implements IZUGFeRDExportableItem {
Charges.add(izac);
return this;
}
public Item addAllowance(IZUGFeRDAllowanceCharge izac) {
Allowances.add(izac);
return this;
}
public Item addNote(String text) {
if (notes==null) {
notes=new ArrayList<String>();
}
notes.add(text);
return this;
}
}

View File

@@ -33,6 +33,11 @@ import java.util.Date;
import org.mustangproject.FileAttachment;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
/***
* the interface of an transaction, e.g. an invoice, you want to create xml (potentially to be added to a PDF)
* for
* @see Invoice if you want to use an object rather than an interface
*/
public interface IExportableTransaction {
/**
@@ -426,13 +431,26 @@ public interface IExportableTransaction {
return null;
}
/***
* specify delivery date
* @return the delivery date
*/
default Date getOccurrenceDate() {
return null;
}
/***
* specify delivery period
* @return the beginning of the delivery period
*/
default Date getOccurrencePeriodFrom() {
return null;
}
/***
* specify delivery period
* @return the end of the delivery period
*/
default Date getOccurrencePeriodTo() {
return null;
}
@@ -446,4 +464,13 @@ public interface IExportableTransaction {
default FileAttachment[] getAdditionalReferencedDocuments() {
return null;
}
/***
* additional text description
* @return an array of strings of document wide "includedNotes" (descriptive text values)
*/
default String[] getNotes() {
return null;
}
}

View File

@@ -34,8 +34,16 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
IZUGFeRDExportableProduct getProduct();
/**
* item level discounts
* @return array of the discounts on a single item
*/
IZUGFeRDAllowanceCharge[] getItemAllowances();
/**
* item level price additions
* @return array of the additional charges on the item
*/
IZUGFeRDAllowanceCharge[] getItemCharges();
@@ -70,8 +78,20 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
return TaxCategoryCodeTypeConstants.STANDARDRATE;
}
/***
* the ID of an additionally referenced document for this item
* @return the id as string
*/
default String getAdditionalReferencedDocumentID() {
return null;
}
/***
* descriptive texts
* @return an array of strings of item specific "includedNotes", text values
*/
default String[] getNotes() {
return null;
}
}

View File

@@ -33,25 +33,88 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
return res;
}
protected BigDecimal getCharges() {
/***
* returns total of charges for this tax rate
* @param percent a specific rate, or null for any rate
* @return the total amount
*/
protected BigDecimal getChargesForPercent(BigDecimal percent) {
BigDecimal res = new BigDecimal(0);
IZUGFeRDAllowanceCharge[] charges = trans.getZFCharges();
if ((charges != null) && (charges.length > 0)) {
for (IZUGFeRDAllowanceCharge currentCharge : charges) {
if ((percent==null)||(currentCharge.getTaxPercent().equals(percent))) {
res = res.add(currentCharge.getTotalAmount(this));
}
}
}
return res;
}
protected BigDecimal getAllowances() {
/***
* returns a (potentially concatenated) string of charge reasons, or "Charges" if none are defined
* @param percent a specific rate, or null for any rate
* @return the space separated String
*/
protected String getChargeReasonForPercent(BigDecimal percent) {
String res = " ";
IZUGFeRDAllowanceCharge[] charges = trans.getZFCharges();
if ((charges != null) && (charges.length > 0)) {
for (IZUGFeRDAllowanceCharge currentCharge : charges) {
if ((percent==null)||(currentCharge.getTaxPercent().equals(percent))) {
if (currentCharge.getReason()!=null) {
res = res+currentCharge.getReason()+" ";
}
}
}
}
res=res.substring(0,res.length()-1);
if (res.equals("")) {
res="Charges";
}
return res;
}
/***
* returns a (potentially concatenated) string of allowance reasons, or "Allowances", if none are defined
* @param percent a specific rate, or null for any rate
* @return the space separated String
*/
protected String getAllowanceReasonForPercent(BigDecimal percent) {
String res = " ";
IZUGFeRDAllowanceCharge[] allowances = trans.getZFAllowances();
if ((allowances != null) && (allowances.length > 0)) {
for (IZUGFeRDAllowanceCharge currentAllowance : allowances) {
if ((percent==null)||(currentAllowance.getTaxPercent().equals(percent))) {
if (currentAllowance.getReason()!=null) {
res = res+currentAllowance.getReason()+" ";
}
}
}
}
res=res.substring(0,res.length()-1);
if (res.equals("")) {
res="Allowances";
}
return res;
}
/***
* returns total of allowances for this tax rate
* @param percent a specific rate, or null for any rate
* @return the total amount
*/
protected BigDecimal getAllowancesForPercent(BigDecimal percent) {
BigDecimal res = new BigDecimal(0);
IZUGFeRDAllowanceCharge[] allowances = trans.getZFAllowances();
if ((allowances != null) && (allowances.length > 0)) {
for (IZUGFeRDAllowanceCharge currentAllowance : allowances) {
if ((percent==null)||(currentAllowance.getTaxPercent().equals(percent))) {
res = res.add(currentAllowance.getTotalAmount(this));
}
}
}
return res;
}
@@ -65,7 +128,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
}
protected BigDecimal getTaxBasis() {
BigDecimal res = getTotal().add(getCharges()).subtract(getAllowances());
BigDecimal res = getTotal().add(getChargesForPercent(null)).subtract(getAllowancesForPercent(null));
return res;
}
@@ -83,7 +146,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalculator lc = new LineCalculator(currentItem);
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
trans.getDocumentCode());
currentItem.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);

View File

@@ -15,7 +15,7 @@ import java.io.*;
public class XMLUpgrader {
static final ClassLoader CLASS_LOADER = XMLUpgrader.class.getClassLoader();
private static final String RESOURCE_PATH = ""; //$NON-NLS-1$
private static final String RESOURCE_PATH = "";
private TransformerFactory mFactory = null;
private Templates mXsltTemplate = null;

View File

@@ -107,7 +107,6 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
this.calc=new TransactionCalculator(trans);
boolean hasDueDate=false;
String taxCategoryCode="";
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy");
String exemptionReason="";
@@ -182,51 +181,13 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
}
xml = xml + " <ram:SellerTradeParty>\n";
if (trans.getOwnForeignOrganisationID()!=null) {
xml = xml + " <ram:ID>" + XMLTools.encodeXML(trans.getOwnForeignOrganisationID()) + "</ram:ID>\n";
}
if ((trans.getSender()!=null)&&(trans.getSender().getGlobalID()!=null)&&(trans.getSender().getGlobalIDScheme()!=null)) {
xml = xml + " <ram:GlobalID schemeID=\"" + XMLTools.encodeXML(trans.getSender().getGlobalIDScheme()) + "\">"
+ XMLTools.encodeXML(trans.getSender().getGlobalID()) + "</ram:GlobalID>\n";
}
xml = xml + " <ram:Name>" + XMLTools.encodeXML(trans.getOwnOrganisationName()) + "</ram:Name>\n"; //$NON-NLS-2$
if (trans.getSender().getContact() != null) {
xml = xml + "<ram:DefinedTradeContact>\n" + " <ram:PersonName>" + XMLTools.encodeXML(trans.getSender().getContact().getName())
+ "</ram:PersonName>\n";
if (trans.getSender().getContact().getPhone() != null) {
xml = xml + " <ram:TelephoneUniversalCommunication>\n" + " <ram:CompleteNumber>"
+ XMLTools.encodeXML(trans.getSender().getContact().getPhone()) + "</ram:CompleteNumber>\n"
+ " </ram:TelephoneUniversalCommunication>\n";
}
if (trans.getSender().getContact().getEMail() != null) {
xml = xml + " <ram:EmailURIUniversalCommunication>\n" + " <ram:URIID>"
+ XMLTools.encodeXML(trans.getSender().getContact().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"
+ " <ram:ID schemeID=\"FC\">" + XMLTools.encodeXML(trans.getOwnTaxID()) + "</ram:ID>\n" //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n"
+ " <ram:SpecifiedTaxRegistration>\n"
+ " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n"
+ " </ram:SellerTradeParty>\n"
xml+= getTradePartyAsXML(trans.getSender(), true);
xml+= " </ram:SellerTradeParty>\n"
+ " <ram:BuyerTradeParty>\n";
// + " <ID>GE2020211</ID>\n"
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
xml+= getTradePartyAsXML(trans.getRecipient());
xml+= getTradePartyAsXML(trans.getRecipient(), false);
if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) {
xml = xml + " <ram:SpecifiedTaxRegistration>\n" + " <ram:ID schemeID=\"VA\">"
+ XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n"
@@ -245,7 +206,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
+ " <ram:ApplicableSupplyChainTradeDelivery>\n" ;
if (this.trans.getDeliveryAddress()!=null) {
xml += "<ram:ShipToTradeParty>"+
getTradePartyAsXML(this.trans.getDeliveryAddress())+
getTradePartyAsXML(this.trans.getDeliveryAddress(), false)+
"</ram:ShipToTradeParty>";
}
@@ -299,7 +260,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
+ " <ram:TypeCode>VAT</ram:TypeCode>\n"
+ exemptionReason
+ " <ram:BasisAmount currencyID=\"" + trans.getCurrency() + "\">" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>\n" // currencyID=\"EUR\"
+ " <ram:CategoryCode>"+taxCategoryCode+"</ram:CategoryCode>\n"
+ " <ram:CategoryCode>"+amount.getCategoryCode()+"</ram:CategoryCode>\n"
+ " <ram:ApplicablePercent>" + vatFormat(currentTaxPercent)
+ "</ram:ApplicablePercent>\n" + " </ram:ApplicableTradeTax>\n"; //$NON-NLS-2$
@@ -356,7 +317,6 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
int lineID = 0;
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
lineID++;
taxCategoryCode=currentItem.getProduct().getTaxCategoryCode();
if (currentItem.getProduct().getTaxExemptionReason() != null) {
exemptionReason="<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
@@ -454,7 +414,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
}
} catch (UnsupportedEncodingException e) {
Logger.getLogger(ZUGFeRD1PullProvider.class.getName()).log(Level.SEVERE, null, e);
} // $NON-NLS-1$
}
}
@Override

View File

@@ -105,7 +105,14 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
return profile;
}
protected String getTradePartyAsXML(IZUGFeRDExportableTradeParty party) {
/***
* returns the UN/CEFACT CII XML for companies(tradeparties), which is actually
* the same for ZF1 (v 2013b) and ZF2 (v 2016b)
* @param party
* @param isSender some attributes are allowed only for senders in certain profiles
* @return
*/
protected String getTradePartyAsXML(IZUGFeRDExportableTradeParty party, boolean isSender) {
String xml = "";
// According EN16931 either GlobalID or seller assigned ID might be present for BuyerTradeParty
// and ShipToTradeParty, but not both. Prefer seller assigned ID for now.
@@ -117,7 +124,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
xml += " <ram:Name>" + XMLTools.encodeXML(party.getName()) + "</ram:Name>\n"; //$NON-NLS-2$
if (party.getContact() != null) {
if ((party.getContact() != null)&&(isSender||profile==Profiles.getByName("Extended"))) {
xml = xml + "<ram:DefinedTradeContact>\n" + " <ram:PersonName>" + XMLTools.encodeXML(party.getContact().getName())
+ "</ram:PersonName>\n";
if (party.getContact().getPhone() != null) {
@@ -175,28 +182,42 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
/***
* returns the XML for a charge or allowance on item level
* @param allowance
* @param item
* @return
*/
protected String getAllowanceChargeStr(IZUGFeRDAllowanceCharge allowance, IAbsoluteValueProvider item) {
String percentage = "";
String chargeIndicator = "false";
if (allowance.getPercent() != null) {
percentage = "<ram:CalculationPercent>" + allowance.getPercent() + "</ram:CalculationPercent>";
if ((allowance.getPercent() != null)&&(profile==Profiles.getByName("Extended"))) {
percentage = "<ram:CalculationPercent>" + vatFormat(allowance.getPercent()) + "</ram:CalculationPercent>";
percentage += "<ram:BasisAmount>" + item.getValue() + "</ram:BasisAmount>";
}
if (allowance.isCharge()) {
chargeIndicator = "true";
}
String allowanceChargeStr = "<ram:AppliedTradeAllowanceCharge><ram:ChargeIndicator><udt:Indicator>" + chargeIndicator + "</udt:Indicator></ram:ChargeIndicator>" + percentage + "<ram:ActualAmount>" + priceFormat(allowance.getTotalAmount(item)) + "</ram:ActualAmount></ram:AppliedTradeAllowanceCharge>";
String reason="";
if ((allowance.getReason()!=null)&&(profile==Profiles.getByName("Extended"))) {
// only in extended profile
reason="<ram:Reason>"+XMLTools.encodeXML(allowance.getReason())+"</ram:Reason>";
}
String allowanceChargeStr = "<ram:AppliedTradeAllowanceCharge><ram:ChargeIndicator><udt:Indicator>" +
chargeIndicator + "</udt:Indicator></ram:ChargeIndicator>" + percentage +
"<ram:ActualAmount>" + priceFormat(allowance.getTotalAmount(item)) + "</ram:ActualAmount>" +
reason+
"</ram:AppliedTradeAllowanceCharge>";
return allowanceChargeStr;
}
@Override
public void generateXML(IExportableTransaction trans) {
this.trans = trans;
this.calc=new TransactionCalculator(trans);
this.calc = new TransactionCalculator(trans);
boolean hasDueDate = false;
String taxCategoryCode = "";
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy");
String exemptionReason = "";
@@ -236,6 +257,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
if (trans.getDocumentCode() != null) {
typecode = trans.getDocumentCode();
}
String notes = "";
if (trans.getNotes() != null) {
for (String currentNote : trans.getNotes()) {
notes = notes + "<ram:IncludedNote><ram:Content>" + XMLTools.encodeXML(currentNote) + "</ram:Content></ram:IncludedNote>";
}
}
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<rsm:CrossIndustryInvoice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:rsm=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100\""
@@ -259,9 +287,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " <ram:TypeCode>" + typecode + "</ram:TypeCode>\n"
+ " <ram:IssueDateTime><udt:DateTimeString format=\"102\">"
+ zugferdDateFormat.format(trans.getIssueDate()) + "</udt:DateTimeString></ram:IssueDateTime>\n" // date
// format
// was
// 20130605
+ notes
+ subjectNote
+ rebateAgreement
+ senderReg
@@ -271,16 +297,23 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
int lineID = 0;
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
lineID++;
taxCategoryCode = currentItem.getProduct().getTaxCategoryCode();
if (currentItem.getProduct().getTaxExemptionReason() != null) {
exemptionReason = "<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
notes = "";
if (currentItem.getNotes() != null) {
for (String currentNote : currentItem.getNotes()) {
notes = notes + "<ram:IncludedNote><ram:Content>" + XMLTools.encodeXML(currentNote) + "</ram:Content></ram:IncludedNote>";
}
}
LineCalculator lc = new LineCalculator(currentItem);
xml = xml + " <ram:IncludedSupplyChainTradeLineItem>\n" +
" <ram:AssociatedDocumentLineDocument>\n"
+ " <ram:LineID>" + lineID + "</ram:LineID>\n" //$NON-NLS-2$
+ notes
+ " </ram:AssociatedDocumentLineDocument>\n"
+ " <ram:SpecifiedTradeProduct>\n";
// + " <GlobalID schemeID=\"0160\">4012345001235</GlobalID>\n"
if (currentItem.getProduct().getSellerAssignedID() != null) {
@@ -363,13 +396,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
xml = xml + " <ram:SellerTradeParty>\n"
+ getTradePartyAsXML(trans.getSender())
+ getTradePartyAsXML(trans.getSender(), true)
+ " </ram:SellerTradeParty>\n"
+ " <ram:BuyerTradeParty>\n";
// + " <ID>GE2020211</ID>\n"
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
xml += getTradePartyAsXML(trans.getRecipient());
xml += getTradePartyAsXML(trans.getRecipient(), false);
xml += " </ram:BuyerTradeParty>\n";
if (trans.getBuyerOrderReferencedDocumentID() != null) {
@@ -403,7 +436,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " <ram:ApplicableHeaderTradeDelivery>\n";
if (this.trans.getDeliveryAddress() != null) {
xml += "<ram:ShipToTradeParty>" +
getTradePartyAsXML(this.trans.getDeliveryAddress()) +
getTradePartyAsXML(this.trans.getDeliveryAddress(), false) +
"</ram:ShipToTradeParty>";
}
@@ -457,7 +490,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " <ram:TypeCode>VAT</ram:TypeCode>\n"
+ exemptionReason
+ " <ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>\n" // currencyID=\"EUR\"
+ " <ram:CategoryCode>" + taxCategoryCode + "</ram:CategoryCode>\n"
+ " <ram:CategoryCode>" + amount.getCategoryCode() + "</ram:CategoryCode>\n"
+ " <ram:RateApplicablePercent>" + vatFormat(currentTaxPercent)
+ "</ram:RateApplicablePercent>\n" + " </ram:ApplicableTradeTax>\n"; //$NON-NLS-2$
@@ -477,36 +510,46 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
if ((trans.getZFCharges() != null) && (trans.getZFCharges().length > 0)) {
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
if (!calc.getChargesForPercent(currentTaxPercent).equals(new BigDecimal(0))) {
xml = xml + " <ram:SpecifiedTradeAllowanceCharge>\n" +
" <ram:ChargeIndicator>\n" +
" <udt:Indicator>true</udt:Indicator>\n" +
" </ram:ChargeIndicator>\n" +
" <ram:ActualAmount>" + currencyFormat(calc.getCharges()) + "</ram:ActualAmount>\n" +
" <ram:Reason>Charge</ram:Reason>\n" +
" <ram:ActualAmount>" + currencyFormat(calc.getChargesForPercent(currentTaxPercent)) + "</ram:ActualAmount>\n" +
" <ram:Reason>"+XMLTools.encodeXML(calc.getChargeReasonForPercent(currentTaxPercent))+"</ram:Reason>\n" +
" <ram:CategoryTradeTax>\n" +
" <ram:TypeCode>VAT</ram:TypeCode>\n" +
" <ram:CategoryCode>S</ram:CategoryCode>\n" +
" <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>\n" +
" <ram:RateApplicablePercent>" + vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent>\n" +
" </ram:CategoryTradeTax>\n" +
" </ram:SpecifiedTradeAllowanceCharge> \n";
}
}
}
if ((trans.getZFAllowances() != null) && (trans.getZFAllowances().length > 0)) {
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
if (!calc.getAllowancesForPercent(currentTaxPercent).equals(new BigDecimal(0))) {
xml = xml + " <ram:SpecifiedTradeAllowanceCharge>\n" +
" <ram:ChargeIndicator>\n" +
" <udt:Indicator>false</udt:Indicator>\n" +
" </ram:ChargeIndicator>\n" +
" <ram:ActualAmount>" + currencyFormat(calc.getAllowances()) + "</ram:ActualAmount>\n" +
" <ram:Reason>Allowance</ram:Reason>\n" +
" <ram:ActualAmount>" + currencyFormat(calc.getAllowancesForPercent(currentTaxPercent)) + "</ram:ActualAmount>\n" +
" <ram:Reason>"+XMLTools.encodeXML(calc.getAllowanceReasonForPercent(currentTaxPercent))+"</ram:Reason>\n" +
" <ram:CategoryTradeTax>\n" +
" <ram:TypeCode>VAT</ram:TypeCode>\n" +
" <ram:CategoryCode>S</ram:CategoryCode>\n" +
" <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>\n" +
" <ram:RateApplicablePercent>" + vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent>\n" +
" </ram:CategoryTradeTax>\n" +
" </ram:SpecifiedTradeAllowanceCharge> \n";
}
}
}
@@ -534,9 +577,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowances()) + "</ram:AllowanceTotalAmount>";
String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowancesForPercent(null)) + "</ram:AllowanceTotalAmount>";
String chargesTotalLine = "<ram:ChargeTotalAmount>" + currencyFormat(calc.getCharges()) + "</ram:ChargeTotalAmount>";
String chargesTotalLine = "<ram:ChargeTotalAmount>" + currencyFormat(calc.getChargesForPercent(null)) + "</ram:ChargeTotalAmount>";
xml = xml + " <ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n"
+ " <ram:LineTotalAmount>" + currencyFormat(calc.getTotal()) + "</ram:LineTotalAmount>\n" //$NON-NLS-2$
@@ -581,7 +624,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
} catch (UnsupportedEncodingException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e);
} // $NON-NLS-1$
}
}
@Override

View File

@@ -490,7 +490,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
pdf.setProducer(fullProducer);
if (ensurePDFisUpgraded) {
try {
pdfaid.setConformance(conformanceLevel.getLetter());// $NON-NLS-1$
pdfaid.setConformance(conformanceLevel.getLetter());
} catch (BadFieldValueException ex) {
// This should be impossible, because it would occur only if an illegal
// conformance level is supplied,

View File

@@ -56,7 +56,7 @@ public class BackwardCompatibilityTest extends TestCase implements IExportableTr
try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setZUGFeRDVersion(1).setProfile("EN16931").load(SOURCE_PDF)) {
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setZUGFeRDVersion(1).setProfile(Profiles.getByName("Extended",1)).load(SOURCE_PDF)) {
ze.setTransaction(this);
ze.disableAutoClose(true);

View File

@@ -44,6 +44,7 @@ public class ZF2PushTest extends TestCase {
final String TARGET_CHARGESALLOWANCESPDF = "./target/testout-ZF2PushChargesAllowances.pdf";
final String TARGET_RELATIVECHARGESALLOWANCESPDF = "./target/testout-ZF2PushRelativeChargesAllowances.pdf";
final String TARGET_ATTACHMENTSPDF = "./target/testout-ZF2PushAttachments.pdf";
final String TARGET_PUSHEDGE = "./target/testout-ZF2PushEdge.pdf";
public void testPushExport() {
@@ -241,6 +242,48 @@ public class ZF2PushTest extends TestCase {
}
public void testPushEdge() {
String orgname = "Test company";
String number = "123";
String priceStr = "1.00";
String taxID = "9990815";
BigDecimal price = new BigDecimal(priceStr);
try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors()
.load(SOURCE_PDF)) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
try {
ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID)).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)), price, new BigDecimal(1.0)).addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))))
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
.setOccurrenceDate(sdf.parse("2020-01-01"))
.setDeliveryDate(sdf.parse("2020-02-02"))
);
} catch (ParseException e) {
e.printStackTrace();
}
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_PUSHEDGE);
} catch (IOException e) {
fail("IOException should not be raised in testEdgeExport");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PUSHEDGE);
assertTrue(zi.getUTF8().contains("item level 1/1"));
assertTrue(zi.getUTF8().contains("document level 2/2"));
}
public void testAllowancesExport() {
String orgname = "Test company";

View File

@@ -118,4 +118,21 @@ public class LibraryTest extends ResourceCase {
}
public void testZF1validity() {
File tempFile = new File("../library/target/testout-MustangGnuaccountingBeispielRE-20171118_506zf1.pdf");
assertTrue(tempFile.exists());
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
String res = zfv.validate(tempFile.getAbsolutePath());
assertThat(res).valueByXPath("/validation/pdf/summary/@status")
.isEqualTo("valid");
assertThat(res).valueByXPath("/validation/xml/summary/@status")
.isEqualTo("valid");
assertThat(res).valueByXPath("/validation/summary/@status")
.isEqualTo("valid");
}
}