diff --git a/library/src/main/java/org/mustangproject/IncludedNote.java b/library/src/main/java/org/mustangproject/IncludedNote.java new file mode 100644 index 00000000..95536afb --- /dev/null +++ b/library/src/main/java/org/mustangproject/IncludedNote.java @@ -0,0 +1,68 @@ +package org.mustangproject; + +/** + * A grouping of business terms to indicate accounting-relevant free texts including a qualification of these. + */ +public class IncludedNote { + private final String content; + private final SubjectCode subjectCode; + + private static final String INCLUDE_START = ""; + private static final String INCLUDE_END = ""; + private static final String CONTENT_START = ""; + private static final String CONTENT_END = ""; + private static final String SUBJECT_CODE_START = ""; + private static final String SUBJECT_CODE_END = ""; + + private IncludedNote(String content, SubjectCode subjectCode) { + this.content = content; + this.subjectCode = subjectCode; + } + + public static IncludedNote generalNote(String content) { + return new IncludedNote(content, SubjectCode.AAI); + } + public static IncludedNote regulatoryNote(String content) { + return new IncludedNote(content, SubjectCode.REG); + } + public static IncludedNote legalNote(String content) { + return new IncludedNote(content, SubjectCode.ABL); + } + public static IncludedNote customsNote(String content) { + return new IncludedNote(content, SubjectCode.CUS); + } + public static IncludedNote sellerNote(String content) { + return new IncludedNote(content, SubjectCode.SUR); + } + public static IncludedNote taxNote(String content) { + return new IncludedNote(content, SubjectCode.TXD); + } + public static IncludedNote introductionNote(String content) { + return new IncludedNote(content, SubjectCode.ACY); + } + public static IncludedNote discountBonusNote(String content) { + return new IncludedNote(content, SubjectCode.AAK); + } + + public static IncludedNote unspecifiedNote(String content) { + return new IncludedNote(content, null); + } + + public String getContent() { + return content; + } + + public SubjectCode getSubjectCode() { + return subjectCode; + } + + public String toCiiXml(){ + String result = INCLUDE_START + CONTENT_START + + XMLTools.encodeXML(getContent() )+ CONTENT_END; + if (getSubjectCode() != null) { + result += SUBJECT_CODE_START + getSubjectCode() + SUBJECT_CODE_END; + } + return result + INCLUDE_END; + } + +} diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 3622b041..da43eaeb 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -22,7 +22,9 @@ package org.mustangproject; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; +import java.util.List; import org.mustangproject.ZUGFeRD.IExportableTransaction; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; @@ -48,6 +50,7 @@ public class Invoice implements IExportableTransaction { @JsonDeserialize(contentAs=Item.class) protected ArrayList ZFItems = null; protected ArrayList notes = null; + private List includedNotes = null; protected String sellerOrderReferencedDocumentID; protected String contractReferencedDocument = null; protected ArrayList xmlEmbeddedFiles=null; @@ -396,7 +399,12 @@ public class Invoice implements IExportableTransaction { return notes.toArray(new String[0]); } - @Override + @Override + public List getNotesWithSubjectCode() { + return includedNotes; + } + + @Override public String getCurrency() { return currency; } @@ -659,8 +667,8 @@ public class Invoice implements IExportableTransaction { } - /*** - * adds a free text paragraph, which will become a includedNote element + /** + * adds a free text paragraph, which will become an includedNote element * @param text freeform UTF8 plain text * @return fluent setter */ @@ -671,7 +679,129 @@ public class Invoice implements IExportableTransaction { notes.add(text); return this; } + + public Invoice addNotes(Collection notes) { + if (notes == null) { + return this; + } + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.addAll(notes); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#AAI} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addGeneralNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.generalNote(content)); + return this; + } + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#REG} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addRegulatoryNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.regulatoryNote(content)); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#ABL} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addLegalNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.legalNote(content)); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#CUS} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addCustomsNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.customsNote(content)); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#SUR} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addSellerNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.sellerNote(content)); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#TXD} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addTaxNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.taxNote(content)); + return this; + } + + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#ACY} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addIntroductionNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.introductionNote(content)); + return this; + } + /** + * adds a free text paragraph, which will become an includedNote element with explicit + * subjectCode {@link SubjectCode#AAK} + * @param content freeform UTF8 plain text + * @return fluent setter + */ + public Invoice addDiscountBonusNote(String content) { + if (includedNotes == null) { + includedNotes = new ArrayList<>(); + } + includedNotes.add(IncludedNote.discountBonusNote(content)); + return this; + } + @Override public String getSpecifiedProcuringProjectID() { return specifiedProcuringProjectID; diff --git a/library/src/main/java/org/mustangproject/SubjectCode.java b/library/src/main/java/org/mustangproject/SubjectCode.java new file mode 100644 index 00000000..99208128 --- /dev/null +++ b/library/src/main/java/org/mustangproject/SubjectCode.java @@ -0,0 +1,40 @@ +package org.mustangproject; + +/** + * EN16931-ID: BT-21 - the qualification of the free text on the invoice from BT-22
+ * In the first step only the recommended codes are implemented. + */ +public enum SubjectCode { + /** + * general information + */ + AAI, + /** + * seller notes + */ + SUR, + /** + * regulatory information + */ + REG, + /** + * legal information + */ + ABL, + /** + * tax information + */ + TXD, + /** + * Customs information + */ + CUS, + /** + * introduction + */ + ACY, + /** + * Discount and bonus agreements + */ + AAK +} diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java index 8fbaf975..36a564bc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java @@ -21,15 +21,10 @@ package org.mustangproject.ZUGFeRD; import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE; -import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE; -import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON; import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.text.SimpleDateFormat; import java.util.Base64; -import java.util.Date; -import java.util.Map; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -45,37 +40,12 @@ public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider @Override public void generateXML(IExportableTransaction trans) { this.trans = trans; - boolean hasDueDate = false; - final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); - - final String exemptionReason = ""; - - String senderReg = ""; - if (trans.getOwnOrganisationFullPlaintextInfo() != null) { - senderReg = "" - + XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + "" - + "REG"; - - } - - String subjectNote = ""; - if (trans.getSubjectNote() != null) { - subjectNote = "" - + XMLTools.encodeXML(trans.getSubjectNote()) + "" - + ""; - } final String typecode = "220"; /*if (trans.getDocumentCode() != null) { typecode = trans.getDocumentCode(); }*/ - String notes = ""; - if (trans.getNotes() != null) { - for (final String currentNote : trans.getNotes()) { - notes += "" + XMLTools.encodeXML(currentNote) + ""; - } - } String testBooleanStr="true"; String xml = "" + typecode + "" + "" + DATE.udtFormat(trans.getIssueDate()) + "" // date - + notes - + subjectNote - + senderReg + + buildNotes(trans) + "" + ""; @@ -111,18 +79,11 @@ public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider if (currentItem.getProduct().getTaxExemptionReason() != null) { // exemptionReason = "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; } - notes = ""; - if (currentItem.getNotes() != null) { - for (final String currentNote : currentItem.getNotes()) { - notes = notes + "" + XMLTools.encodeXML(currentNote) + ""; - - } - } - final LineCalculator lc = new LineCalculator(currentItem); + final LineCalculator lc = new LineCalculator(currentItem); xml += "" + "" + "" + lineID + "" - + notes + + buildItemNotes(currentItem) + "" + ""; @@ -308,51 +269,17 @@ public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider return profile; } - private String buildPaymentTermsXml() { - - final IZUGFeRDPaymentTerms paymentTerms = trans.getPaymentTerms(); - if (paymentTerms == null) { - return ""; - } - String paymentTermsXml = ""; - - final IZUGFeRDPaymentDiscountTerms discountTerms = paymentTerms.getDiscountTerms(); - final Date 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 += "" + paymentTerms.getDescription() + ""; - if (dueDate != null) { - paymentTermsXml += ""; - paymentTermsXml += DATE.udtFormat(dueDate); - paymentTermsXml += ""; - } - - if (discountTerms != null) { - paymentTermsXml += ""; - final String currency = trans.getCurrency(); - final String basisAmount = currencyFormat(calc.getGrandTotal()); - paymentTermsXml += "" + basisAmount + ""; - paymentTermsXml += "" + discountTerms.getCalculationPercentage().toString() - + ""; - - if (discountTerms.getBaseDate() != null) { - final Date baseDate = discountTerms.getBaseDate(); - paymentTermsXml += ""; - paymentTermsXml += DATE.udtFormat(baseDate); - paymentTermsXml += ""; - - paymentTermsXml += "" - + discountTerms.getBasePeriodMeasure() + ""; - } - - paymentTermsXml += ""; - } - - paymentTermsXml += ""; - return paymentTermsXml; - } - - + @Override + protected String buildNotes(IExportableTransaction exportableTransaction) { + Invoice copyWithoutRebateInfo = new Invoice() + .setOwnOrganisationFullPlaintextInfo(exportableTransaction.getOwnOrganisationFullPlaintextInfo()) + .addNotes(exportableTransaction.getNotesWithSubjectCode()); + if(exportableTransaction.getNotes() != null) { + for (String note : exportableTransaction.getNotes()) { + copyWithoutRebateInfo.addNote(note); + } + } + Optional.ofNullable(exportableTransaction.getSubjectNote()).ifPresent(copyWithoutRebateInfo::addNote); + return super.buildNotes(copyWithoutRebateInfo); + } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java index fb8fa602..bd778d78 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java @@ -29,8 +29,10 @@ package org.mustangproject.ZUGFeRD; import java.math.BigDecimal; import java.util.Date; +import java.util.List; import org.mustangproject.FileAttachment; +import org.mustangproject.IncludedNote; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; /*** @@ -80,10 +82,13 @@ public interface IExportableTransaction { /** * 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. - *

+ *

* Lieferant GmbH Lieferantenstraße 20 80333 München Deutschland * Geschäftsführer: Hans Muster Handelsregisternummer: H A 123 + *

* + * It is written as an includedNode with subjectCode {@link org.mustangproject.SubjectCode#REG}. See also + * {@link #getNotesWithSubjectCode()} * @return null or full sender institution name, details, manager and tax * registration */ @@ -489,6 +494,13 @@ public interface IExportableTransaction { return null; } + /** + * A grouping of business terms to indicate accounting-relevant free texts including a qualification of these.

+ * The information are written to the same xml nodes like {@link #getNotes()} but with explicit subjectCode. + */ + default List getNotesWithSubjectCode(){ + return null; + } default String getSpecifiedProcuringProjectName() { return null; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java index 10b4b498..9ea90e19 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java @@ -27,14 +27,18 @@ import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATE import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Base64; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.mustangproject.EStandard; import org.mustangproject.FileAttachment; +import org.mustangproject.IncludedNote; import org.mustangproject.XMLTools; public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider { @@ -64,39 +68,11 @@ public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider } - String senderReg = ""; - if (trans.getOwnOrganisationFullPlaintextInfo() != null) { - senderReg = "" - + XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + "" - + "REG"; - - } - - String rebateAgreement = ""; - if (trans.rebateAgreementExists()) { - rebateAgreement = "" - + "Es bestehen Rabatt- und Bonusvereinbarungen." - + "AAK"; - } - - String subjectNote = ""; - if (trans.getSubjectNote() != null) { - subjectNote = "" - + XMLTools.encodeXML(trans.getSubjectNote()) + "" - + ""; - } - final String typecode = "220"; /*if (trans.getDocumentCode() != null) { typecode = trans.getDocumentCode(); }*/ - String notes = ""; - if (trans.getNotes() != null) { - for (final String currentNote : trans.getNotes()) { - notes += "" + XMLTools.encodeXML(currentNote) + ""; - - } - } + String xml = "" + "" + typecode + "" + "" + DATE.udtFormat(trans.getIssueDate()) + "" // date - + notes - + subjectNote - + rebateAgreement - + senderReg + + buildNotes(trans) + "" + ""; @@ -139,18 +112,12 @@ public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider if (currentItem.getProduct().getTaxExemptionReason() != null) { // exemptionReason = "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; } - notes = ""; - if (currentItem.getNotes() != null) { - for (final String currentNote : currentItem.getNotes()) { - notes = notes + "" + XMLTools.encodeXML(currentNote) + ""; - - } - } + final LineCalculator lc = new LineCalculator(currentItem); xml += "" + "" + "" + lineID + "" - + notes + + buildItemNotes(currentItem) + "" + ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 912e35e1..dd7eb3d3 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -29,11 +29,16 @@ import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Date; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.dom4j.Document; import org.dom4j.DocumentException; @@ -41,6 +46,7 @@ import org.dom4j.DocumentHelper; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.mustangproject.FileAttachment; +import org.mustangproject.IncludedNote; import org.mustangproject.XMLTools; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; @@ -277,7 +283,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { boolean hasDueDate = false; final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); - + String exemptionReason = ""; if (trans.getPaymentTermDescription() != null) { @@ -288,42 +294,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { paymentTermsDescription = "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate()); } - - String senderReg = ""; - if (trans.getOwnOrganisationFullPlaintextInfo() != null) { - senderReg = "" - + XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + "" - + "REG"; - - } - - String rebateAgreement = ""; - if (trans.rebateAgreementExists()) { - rebateAgreement = "" - + "Es bestehen Rabatt- und Bonusvereinbarungen." - + "AAK"; - } - - String subjectNote = ""; - if (trans.getSubjectNote() != null) { - subjectNote = "" - + XMLTools.encodeXML(trans.getSubjectNote()) + "" - + ""; - } - + String typecode = "380"; if (trans.getDocumentCode() != null) { typecode = trans.getDocumentCode(); } - String notes = ""; - if (trans.getNotes() != null) { - for (final String currentNote : trans.getNotes()) { - notes = notes + "" + XMLTools.encodeXML(currentNote) + ""; - - } - } - String xml = "" - + String xml = "" + "" + typecode + "" + "" + DATE.udtFormat(trans.getIssueDate()) + "" // date - + notes - + subjectNote - + rebateAgreement - + senderReg + + buildNotes(trans) + "" + ""; @@ -360,20 +333,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if (currentItem.getProduct().getTaxExemptionReason() != null) { exemptionReason = "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; } - notes = ""; - if (currentItem.getNotes() != null) { - for (final String currentNote : currentItem.getNotes()) { - notes = notes + "" + XMLTools.encodeXML(currentNote) + ""; - - } - } final LineCalculator lc = new LineCalculator(currentItem); - if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) { - - xml += "" + + if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) { + xml += "" + "" + "" + lineID + "" - + notes + + buildItemNotes(currentItem) + "" + ""; @@ -422,16 +387,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { "" + XMLTools.encodeXML(currentReferencedDocument.getTypeCode()) + "" + "" + XMLTools.encodeXML(currentReferencedDocument.getReferenceTypeCode()) + "" + ""; - - } - } if (currentItem.getBuyerOrderReferencedDocumentLineID() != null) { xml += " " + "" + XMLTools.encodeXML(currentItem.getBuyerOrderReferencedDocumentLineID()) + "" + ""; - } xml += "" + "" + priceFormat(lc.getPriceGross()) @@ -647,8 +608,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += "" + DATE.udtFormat(trans.getDetailedDeliveryPeriodTo()) + ""; } xml += ""; - - } if ((trans.getZFCharges() != null) && (trans.getZFCharges().length > 0)) { @@ -776,7 +735,37 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } } - @Override + protected String buildItemNotes(IZUGFeRDExportableItem currentItem) { + if (currentItem.getNotes() == null) { + return ""; + } + return Arrays.stream(currentItem.getNotes()) + .map(IncludedNote::unspecifiedNote) + .map(IncludedNote::toCiiXml) + .collect(Collectors.joining()); + } + + protected String buildNotes(IExportableTransaction exportableTransaction) { + final List includedNotes = Optional.ofNullable(exportableTransaction.getNotesWithSubjectCode()) + .orElse(new ArrayList<>()); + if (exportableTransaction.getNotes() != null) { + for (final String currentNote : exportableTransaction.getNotes()) { + includedNotes.add(IncludedNote.unspecifiedNote(currentNote)); + } + } + if (exportableTransaction.rebateAgreementExists()) { + includedNotes.add(IncludedNote.discountBonusNote("Es bestehen Rabatt- und Bonusvereinbarungen.")); + } + Optional.ofNullable(exportableTransaction.getOwnOrganisationFullPlaintextInfo()) + .ifPresent(info -> includedNotes.add(IncludedNote.regulatoryNote(info))); + + Optional.ofNullable(exportableTransaction.getSubjectNote()) + .ifPresent(note -> includedNotes.add(IncludedNote.unspecifiedNote(note))); + + return includedNotes.stream().map(IncludedNote::toCiiXml).collect(Collectors.joining("")); + } + + @Override public void setProfile(Profile p) { profile = p; }