Skip to content

Commit

Permalink
Add support for prefixed UBL invoice and credit note XML (#8)
Browse files Browse the repository at this point in the history
* Add support for prefixed UBL invoice and credit note XML
  • Loading branch information
coreequip authored Jul 22, 2024
1 parent f8d05bf commit f4f4f19
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 17 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
currentVersion=0.1.4
currentVersion=0.1.5
mainClassName=io.github.easybill.xrviz.App
27 changes: 17 additions & 10 deletions src/main/java/io/github/easybill/xrviz/XslTransformer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.easybill.xrviz;

import net.sf.saxon.regex.RegularExpression;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
Expand All @@ -23,13 +24,16 @@
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XslTransformer {
static final Logger logger = Logger.getGlobal();
static final String BASE_PATH = Config.getValue(Config.Keys.DATA_PATH);
public static final String CII_VALIDATION_STRING = "<rsm:CrossIndustryInvoice";
public static final String UBL_I_VALIDATION_STRING = "<Invoice";
public static final String UBL_C_VALIDATION_STRING = "<CreditNote";
public static final String CII_VALIDATION_STRING = "CrossIndustryInvoice";
public static final String UBL_I_VALIDATION_STRING = "Invoice";
public static final String UBL_C_VALIDATION_STRING = "CreditNote";
public static final Pattern REGEX = Pattern.compile("[<:](CrossedustryInvoice|Invoice|CreditNote)");

enum DocumentType {
CII("cii-xr.xsl"),
Expand All @@ -51,15 +55,18 @@ public static Optional<DocumentType> detectDocumentType(String xmlContent) {
return Optional.empty();
}

if (xmlContent.contains(CII_VALIDATION_STRING)) {
return Optional.of(CII);
} else if (xmlContent.contains(UBL_I_VALIDATION_STRING)) {
return Optional.of(UBL_I);
} else if (xmlContent.contains(UBL_C_VALIDATION_STRING)) {
return Optional.of(UBL_C);
final Matcher matcher = REGEX.matcher(xmlContent);
if (!matcher.find()) {
return Optional.empty();
}

return Optional.empty();
return switch (matcher.group(1)) {
case CII_VALIDATION_STRING -> Optional.of(CII);
case UBL_I_VALIDATION_STRING -> Optional.of(UBL_I);
case UBL_C_VALIDATION_STRING -> Optional.of(UBL_C);
default -> Optional.empty();
};

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

import java.net.HttpURLConnection;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import static io.github.easybill.xrviz.XslTransformer.*;

public abstract class XmlRequestExtractor {
static final Logger logger = Logger.getGlobal();
static final Pattern REGEX = Pattern.compile("[<:](CrossedustryInvoice|Invoice|CreditNote)");

Optional<String> validate(HttpExchange exchange) throws IOException {
if (!exchange.getRequestMethod().equalsIgnoreCase("POST")) {
Expand Down Expand Up @@ -40,8 +42,6 @@ String getLanguage(HttpExchange exchange) {
}

private boolean isXMLValid(String xml) {
return !xml.isBlank() && (xml.contains(CII_VALIDATION_STRING) ||
xml.contains(UBL_I_VALIDATION_STRING) ||
xml.contains(UBL_C_VALIDATION_STRING));
return !xml.isBlank() && (REGEX.matcher(xml).find());
}
}
7 changes: 7 additions & 0 deletions src/test/http/api-test.http
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ Accept-Language: de

< ./ubl-invoice.xml

### Generate a HTML file from a prefixed UBL Invoice file
POST {{baseUrl}}/convert.html
Content-Type: application/xml
Accept-Language: de

< ./ubl-invoice-prefix.xml

### Generate a HTML file from a UBL CreditNote file
POST {{baseUrl}}/convert.html
Content-Type: application/xml
Expand Down
145 changes: 145 additions & 0 deletions src/test/http/ubl-invoice-prefix.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>123456XX</cbc:ID>
<cbc:IssueDate>2016-04-04</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>#ADU#Es gelten unsere Allgem. Geschäftsbedingungen, die Sie unter […] finden.</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="EM">[email protected]</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>[Seller trading name]</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>[Seller address line 1]</cbc:StreetName>
<cbc:CityName>[Seller city]</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE 123456789</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>[Seller name]</cbc:RegistrationName>
<cbc:CompanyID>[HRA-Eintrag]</cbc:CompanyID>
<cbc:CompanyLegalForm>123/456/7890, HRA-Eintrag in […]</cbc:CompanyLegalForm>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:Name>nicht vorhanden</cbc:Name>
<cbc:Telephone>+49 1234-5678</cbc:Telephone>
<cbc:ElectronicMail>[email protected]</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="EM">[email protected]</cbc:EndpointID>
<cac:PartyIdentification>
<cbc:ID>[Buyer identifier]</cbc:ID>
</cac:PartyIdentification>
<cac:PostalAddress>
<cbc:StreetName>[Buyer address line 1]</cbc:StreetName>
<cbc:CityName>[Buyer city]</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>[Buyer name]</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>58</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<!-- dies ist eine nicht existerende aber valide IBAN als test dummy -->
<cbc:ID>DE75512108001245126199</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Zahlbar sofort ohne Abzug.</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">22.04</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">314.86</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">22.04</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">314.86</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">314.86</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">336.9</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">336.9</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>Zeitschrift [...]</cbc:ID>
<cbc:Note>Die letzte Lieferung im Rahmen des abgerechneten Abonnements erfolgt in 12/2016 Lieferung erfolgt / erfolgte direkt vom Verlag</cbc:Note>
<cbc:InvoicedQuantity unitCode="XPP">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">288.79</cbc:LineExtensionAmount>
<cac:InvoicePeriod>
<cbc:StartDate>2016-01-01</cbc:StartDate>
<cbc:EndDate>2016-12-31</cbc:EndDate>
</cac:InvoicePeriod>
<cac:OrderLineReference>
<cbc:LineID>6171175.1</cbc:LineID>
</cac:OrderLineReference>
<cac:Item>
<cbc:Description>Zeitschrift Inland</cbc:Description>
<cbc:Name>Zeitschrift [...]</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>246</cbc:ID>
</cac:SellersItemIdentification>
<cac:CommodityClassification>
<cbc:ItemClassificationCode listID="IB">0721-880X</cbc:ItemClassificationCode>
</cac:CommodityClassification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">288.79</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>Porto + Versandkosten</cbc:ID>
<cbc:InvoicedQuantity unitCode="XPP">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">26.07</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Porto + Versandkosten</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">26.07</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</ubl:Invoice>
6 changes: 3 additions & 3 deletions version-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f4f4f19

Please sign in to comment.