Skip to content

Latest commit

 

History

History
185 lines (148 loc) · 7.61 KB

README.md

File metadata and controls

185 lines (148 loc) · 7.61 KB

Maven Central License

Peppol bis billing 3 generator

This is a Pojo implementation of https://docs.peppol.eu/poacc/billing/3.0/.

The principle is that the domain classes in plain java encode naming and cardinality and they can produce xml from itself.

We use eaxy as an XML generator because of it ease of use and no dependency nature.

We use phive-api for optional validation.

This is Java 11.

Installation

If you just want to create the xml, you only need the api:

<dependency>
    <groupId>no.digipost</groupId>
    <artifactId>peppol-bis-billing-3-generator-api</artifactId>
    <version>x.y.z</version>
</dependency>

You then start from Invoice and create add the content yourself. You need to know what to add and how to build the different domain constructs. Ie. you need to know what Peppol Billing is and what is expected to be present. Luckily the documentation for the standard is very good. Take a look at https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/tree/ for comprehensive documentation on all the fields.

  • We will be adding more country spesific object-builders on top of the basic API as need emerges.

Check out the example below.

Validation

If you want, you can validate the constructed Peppol Billing xml right here without utilizing web pages where you upload an xml file and you get the response. You can put this validation into you Unit-testing of your code!

You need the validation-artifact:

<dependency>
    <groupId>no.digipost</groupId>
    <artifactId>peppol-bis-billing-3-generator-validation</artifactId>
    <version>x.y.z</version>
</dependency>

In api there is a Validate class that takes an Invoice as parameter. With the above depencency on classpath the validation kicks in and returns a ValidationResult stating whether the xml is valid Peppol Billing 3 or not and with failures and/or error-information.

Eksample. error (From InvoiceApiTest):

final Invoice invoice = ExampleUsage1.example1();
final ValidationResult result = new Validate(invoice).result();

//[fatal_error] [UBL-SR-47] in /:Invoice[1] [UBL-SR-47]-When there are more than one payment means code, they shall be equal Test=count(//cbc:PaymentMeansCode[not(preceding::cbc:PaymentMeansCode/. = .)])

Beware that default validation is done with the a given specification. We use phive-rules-peppol for validation. You can utilize this to change the rules that apply for your specific needs by specifying the VESID that is in use. You can do this by setting a static field in DefaultPeppolBilling3Validation. We do this in a test called PeppolBillingpiTest:

// This is the latest as of this writing and is 3.0.9
DefaultPeppolBilling3Validation.setVesid(PeppolValidation3_11_1.VID_OPENPEPPOL_INVOICE_V3);

You can use for instance (version 3.0.5+hotfix):
DefaultPeppolBilling3Validation.setVesid(PeppolValidation391.VID_OPENPEPPOL_INVOICE_V3);

Your accesspoint will probably do some kind of validation accoring to a given peppol version billing. You should update your unit-tests accordingly to meet the correct setup requirements.

Example of Invoice creation

// Create an Invoice with all your data. This is a low level non complete example.

final AccountingSupplierParty accountingSupplierParty = new AccountingSupplierParty(
    new Party(
        new EndpointID("916725280").withSchemeID("0192")
        , new PostalAddress(new Country("NO")).withStreetName("Oslogate 1").withCityName("Oslo").withPostalZone("0342")
        , new PartyLegalEntity("Acme Cargo AS").withCompanyID(new CompanyID("916725280").withSchemeID("0192"))
    ).withPartyIdentification(new PartyIdentification(new ID("916725280")))
        .withPartyName(new PartyName("PartyName"))
        .withPartyTaxScheme(new PartyTaxScheme("NO" + "916725280" +  "MVA", new TaxScheme("VAT")))
        .withPartyTaxScheme(new PartyTaxScheme("Foretaksregisteret", new TaxScheme("TAX")))
        .withContact(new Contact().withTelephone("04321 (abroad +47 12341234)"))
);

final AccountingCustomerParty accountingCustomerParty = new AccountingCustomerParty(
    new Party(
        new EndpointID("916725280").withSchemeID("0192")
        , new PostalAddress(new Country("NO")).withStreetName("Sandnesgate 3").withCityName("Sandnes").withPostalZone("4313")
        , new PartyLegalEntity("Acme As").withCompanyID(new CompanyID("916725280").withSchemeID("0192"))
    ).withPartyIdentification(new PartyIdentification(new ID("10030177835")))
        .withPartyName(new PartyName("Acme As"))
        .withPartyTaxScheme(new PartyTaxScheme("NO916725280MVA", new TaxScheme("VAT")))
);

final TaxTotal taxTotal = new TaxTotal(
    new TaxAmount("0.00", "NOK")
).withTaxSubtotal(new TaxSubtotal(
    new TaxableAmount("2860.00", "NOK")
    , new TaxAmount("0.00", "NOK")
    , new TaxCategory("G", new TaxScheme("VAT"))
    .withPercent("0.000")
    .withTaxExemptionReason("Utførsel av varer og tjenester, 0 %")
));

final LegalMonetaryTotal legalMonetaryTotal = new LegalMonetaryTotal(
    new LineExtensionAmount("2860.00", "NOK")
    , new TaxExclusiveAmount("2860.00", "NOK")
    , new TaxInclusiveAmount("2860.00", "NOK")
    , new PayableAmount("2860.00", "NOK")
);

final InvoiceLine invoiceLine = new InvoiceLine(
    "1", new InvoicedQuantity("1", "STK")
    , new LineExtensionAmount("2860.00", "NOK")
    , new Item(
    "Frakt", new ClassifiedTaxCategory("G", new TaxScheme("VAT")).withPercent("0.000")
).withSellersItemIdentification(new SellersItemIdentification("101366"))
    , new Price(new PriceAmount("2860.00", "NOK"))
);

final Delivery delivery = new Delivery().withDeliveryLocation(
    new DeliveryLocation().withAddress(
        new Address(new Country("NO")).withAdditionalStreetName("Sandnesgate 3")
            .withCityName("Sandnes")
            .withPostalZone("4313")
    )
).withDeliveryParty(new DeliveryParty(new PartyName("Acme As")));


final PaymentMeans paymentMeans1 = new PaymentMeans(
    new PaymentMeansCode("58").withName("SEPA credit transfer")
).withPaymentID("123456789101")
    .withPayeeFinancialAccount(
        new PayeeFinancialAccount("NO6960650514745")
            .withFinancialInstitutionBranch(new FinancialInstitutionBranch("NDEANOKK"))
    );

final Invoice invoice = new Invoice(
    "12345678910"
    , "2020-11-19"
    , "NOK"
    , accountingSupplierParty
    , accountingCustomerParty
    , taxTotal
    , legalMonetaryTotal
    , Collections.singletonList(invoiceLine)
).withBuyerReference("n/a")
    .withDueDate("2020-11-30")
    .withPaymentTerms(new PaymentTerms("10 dager"))
    .withDelivery(delivery)
    .withPaymentMeans(paymentMeans1);


// Perform validation (if you add peppol-bis-billing-3-generator-validation on classpath

final PeppolBillingApi<Invoice> api = PeppolBillingApi.create(invoice);
final ValidationResult validationResult = api.validate();

if(validationResult.isValid()){
    // You have a valid peppol billing file and can either print it to file from string or 
    // send it to Digipost through the ApiClient
    final String xml = api.prettyPrint();
    api.inputStream();
} else {
    // You have validation errors and you need to check them an correct them
    validationResult.errors().forEach(System.out::println);
    validationResult.warns().forEach(System.out::println);
}