ZUGFeRD Invoice Example

This is a code example of iText PDF, discover more.

1st September 2017
admin-marketing

Switch code for this example

Customer.java
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;

/**
 * Plain Old Java Object containing info about a Customer.
 * @author Bruno Lowagie (iText Software)
 */
public class Customer {
    
    /** The id. */
    protected int id;
    
    /** The first name. */
    protected String firstName;
    
    /** The last name. */
    protected String lastName;
    
    /** The street. */
    protected String street;
    
    /** The postal code. */
    protected String postalcode;
    
    /** The city. */
    protected String city;
    
    /** The country id. */
    protected String countryId;

    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the first name.
     *
     * @return the first name
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets the first name.
     *
     * @param firstName the new first name
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * Gets the last name.
     *
     * @return the last name
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Sets the last name.
     *
     * @param lastName the new last name
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * Gets the street.
     *
     * @return the street
     */
    public String getStreet() {
        return street;
    }

    /**
     * Sets the street.
     *
     * @param street the new street
     */
    public void setStreet(String street) {
        this.street = street;
    }

    /**
     * Gets the city.
     *
     * @return the city
     */
    public String getCity() {
        return city;
    }

    /**
     * Sets the city.
     *
     * @param city the new city
     */
    public void setCity(String city) {
        this.city = city;
    }

    /**
     * Gets the postal code.
     *
     * @return the postal code
     */
    public String getPostalcode() {
        return postalcode;
    }

    /**
     * Sets the postal code.
     *
     * @param postalcode the new postalcode
     */
    public void setPostalcode(String postalcode) {
        this.postalcode = postalcode;
    }

    /**
     * Gets the country id.
     *
     * @return the country id
     */
    public String getCountryId() {
        return countryId;
    }

    /**
     * Sets the country id.
     *
     * @param countryId the new country id
     */
    public void setCountryId(String countryId) {
        this.countryId = countryId;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(id).append("\n");
        sb.append("    First Name: ").append(firstName).append("\n");
        sb.append("    Last Name: ").append(lastName).append("\n");
        sb.append("    Street: ").append(street).append("\n");
        sb.append("    City: ").append(countryId).append(" ").append(postalcode).append(" ").append(city);
        return sb.toString();
    }
}
Invoice.java
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;

import java.util.Date;
import java.util.List;

/**
 * Plain Old Java Object containing info about an Invoice.
 * @author Bruno Lowagie (iText Software)
 */
public class Invoice {
    
    /** The id. */
    protected int id;
    
    /** The customer. */
    protected Customer customer;
    
    /** The total. */
    protected double total;
    
    /** The items. */
    protected List items;
    
    /** The invoice date. */
    protected Date invoiceDate;

    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the customer.
     *
     * @return the customer
     */
    public Customer getCustomer() {
        return customer;
    }

    /**
     * Sets the customer.
     *
     * @param customer the new customer
     */
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    /**
     * Gets the total.
     *
     * @return the total
     */
    public double getTotal() {
        return total;
    }

    /**
     * Sets the total.
     *
     * @param total the new total
     */
    public void setTotal(double total) {
        this.total = total;
    }

    /**
     * Gets the items.
     *
     * @return the items
     */
    public List getItems() {
        return items;
    }

    /**
     * Sets the items.
     *
     * @param items the new items
     */
    public void setItems(List items) {
        this.items = items;
    }

    /**
     * Gets the invoice date.
     *
     * @return the invoice date
     */
    public Date getInvoiceDate() {
        return invoiceDate;
    }

    /**
     * Sets the invoice date.
     *
     * @param invoiceDate the new invoice date
     */
    public void setInvoiceDate(Date invoiceDate) {
        this.invoiceDate = invoiceDate;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Invoice id: ").append(id).append(" Date: ").append(invoiceDate).append(" Total cost: ").append(total).append("\u20ac\n");
        sb.append("Customer: ").append(customer.toString()).append("\n");
        for (Item item : items) {
            sb.append(item.toString()).append("\n");
        }
        return sb.toString();
    }
}
Item.java
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;

/**
 * Plain Old Java Object containing info about an Item.
 * @author Bruno Lowagie (iText Software)
 */
public class Item {
    
    /** The item. */
    protected int item;
    
    /** The product. */
    protected Product product;
    
    /** The quantity. */
    protected int quantity;
    
    /** The cost. */
    protected double cost;

    /**
     * Gets the item.
     *
     * @return the item
     */
    public int getItem() {
        return item;
    }

    /**
     * Sets the item.
     *
     * @param item the new item
     */
    public void setItem(int item) {
        this.item = item;
    }

    /**
     * Gets the product.
     *
     * @return the product
     */
    public Product getProduct() {
        return product;
    }

    /**
     * Sets the product.
     *
     * @param product the new product
     */
    public void setProduct(Product product) {
        this.product = product;
    }

    /**
     * Gets the quantity.
     *
     * @return the quantity
     */
    public int getQuantity() {
        return quantity;
    }

    /**
     * Sets the quantity.
     *
     * @param quantity the new quantity
     */
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    /**
     * Gets the cost.
     *
     * @return the cost
     */
    public double getCost() {
        return cost;
    }

    /**
     * Sets the cost.
     *
     * @param cost the new cost
     */
    public void setCost(double cost) {
        this.cost = cost;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("  #").append(item);
        sb.append(product.toString());
        sb.append("\tQuantity: ").append(quantity);
        sb.append("\tCost: ").append(cost).append("\u20ac");
        return sb.toString();
    }
}
PojoFactory.java
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Factory that creates Invoice, Customer, Product, and Item classes.
 * @author Bruno Lowagie (iText Software)
 */
public class PojoFactory {
    
    /** Instance of this PojoFactory that will be reused. */
    protected static PojoFactory factory = null;
    
    /** The connection to the HSQLDB database. */
    protected Connection connection;
    
    /** The customer cache. */
    protected HashMap customerCache = new HashMap();
    
    /** The product cache. */
    protected HashMap productCache = new HashMap();
    
    /** Prepared statement to get customer data. */
    protected PreparedStatement getCustomer;

    /** Prepared statement to get product data. */
    protected PreparedStatement getProduct;

    /** Prepared statement to get items. */
    protected PreparedStatement getItems;
    
    /**
     * Instantiates a new POJO factory.
     *
     * @throws ClassNotFoundException the class not found exception
     * @throws SQLException the SQL exception
     */
    private PojoFactory() throws ClassNotFoundException, SQLException {
        Class.forName("org.hsqldb.jdbcDriver");
        connection = DriverManager.getConnection(
            "jdbc:hsqldb:resources/db/invoices", "SA", "");
        getCustomer = connection.prepareStatement("SELECT * FROM Customer WHERE id = ?");
        getProduct = connection.prepareStatement("SELECT * FROM Product WHERE id = ?");
        getItems = connection.prepareStatement("SELECT * FROM Item WHERE invoiceid = ?");
    }
    
    /**
     * Gets the single instance of PojoFactory.
     *
     * @return single instance of PojoFactory
     * @throws SQLException the SQL exception
     */
    public static PojoFactory getInstance() throws SQLException {
        if (factory == null || factory.connection.isClosed()) {
            try {
                factory = new PojoFactory();
            } catch (ClassNotFoundException cnfe) {
                throw new SQLException(cnfe.getMessage());
            }
        }
        return factory;
    }
    
    /**
     * Close the database connection.
     *
     * @throws SQLException the SQL exception
     */
    public void close() throws SQLException {
        connection.close();
    }
    
    /**
     * Gets all the {@link Invoice} objects stored in the database.
     *
     * @return the invoices
     * @throws SQLException the SQL exception
     */
    public List getInvoices() throws SQLException {
        List invoices = new ArrayList();
        Statement stm = connection.createStatement();
        ResultSet rs = stm.executeQuery("SELECT * FROM Invoice");
        while (rs.next()) {
            invoices.add(getInvoice(rs));
        }
        stm.close();
        return invoices;
    }
    
    /**
     * Creates an {@link Invoice} object from a database result set.
     *
     * @param rs the result set
     * @return the invoice object
     * @throws SQLException the SQL exception
     */
    public Invoice getInvoice(ResultSet rs) throws SQLException {
        Invoice invoice = new Invoice();
        invoice.setId(rs.getInt("id"));
        invoice.setCustomer(getCustomer(rs.getInt("customerid")));
        List items = getItems(rs.getInt("id"));
        invoice.setItems(items);
        double total = 0;
        for (Item item : items)
            total += item.getCost();
        invoice.setTotal(total);
        invoice.setInvoiceDate(rs.getDate("invoicedate"));
        return invoice;
    }
    
    /**
     * Creates an {@link Item} object from a database result set.
     *
     * @param rs the result set
     * @return the item object
     * @throws SQLException the SQL exception
     */
    public Item getItem(ResultSet rs) throws SQLException {
        Item item = new Item();
        item.setItem(rs.getInt("Item"));
        Product product = getProduct(rs.getInt("ProductId"));
        item.setProduct(product);
        item.setQuantity(rs.getInt("Quantity"));
        item.setCost(item.getQuantity() * product.getPrice());
        return item;
    }
    
    /**
     * Gets a {@link Customer} object, given a customer id.
     *
     * @param id the customer id
     * @return the customer object
     * @throws SQLException the SQL exception
     */
    public Customer getCustomer(int id) throws SQLException {
        if (customerCache.containsKey(id))
            return customerCache.get(id);
        getCustomer.setInt(1, id);
        ResultSet rs = getCustomer.executeQuery();
        if (rs.next()) {
            Customer customer = new Customer();
            customer.setId(id);
            customer.setFirstName(rs.getString("FirstName"));
            customer.setLastName(rs.getString("LastName"));
            customer.setStreet(rs.getString("Street"));
            customer.setPostalcode(rs.getString("Postalcode"));
            customer.setCity(rs.getString("City"));
            customer.setCountryId(rs.getString("CountryID"));
            customerCache.put(id, customer);
            return customer;
        }
        return null;
    }
    
    /**
     * Gets a {@link Product} object, given a product id.
     *
     * @param id the product id
     * @return the product object
     * @throws SQLException the SQL exception
     */
    public Product getProduct(int id) throws SQLException {
        if (productCache.containsKey(id))
            return productCache.get(id);
        getProduct.setInt(1, id);
        ResultSet rs = getProduct.executeQuery();
        if (rs.next()) {
            Product product = new Product();
            product.setId(id);
            product.setName(rs.getString("Name"));
            product.setPrice(rs.getDouble("Price"));
            product.setVat(rs.getDouble("Vat"));
            productCache.put(id, product);
            return product;
        }
        return null;
    }
    
    /**
     * Gets a list of {@link Item} objects for a specific invoice.
     *
     * @param invoiceid the invoice id
     * @return the items
     * @throws SQLException the SQL exception
     */
    public List getItems(int invoiceid) throws SQLException {
        List items = new ArrayList();
        getItems.setInt(1, invoiceid);
        ResultSet rs = getItems.executeQuery();
        while (rs.next()) {
            items.add(getItem(rs));
        }
        return items;
    }
}
Product.java
/*
 * Part of a set of classes based on a sample database.
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/3-simple-invoice-database
 */
package com.itextpdf.zugferd.pojo;

/**
 * Plain Old Java Object containing info about a Product.
 * @author Bruno Lowagie (iText Software)
 */
public class Product {
    
    /** The id. */
    protected int id;
    
    /** The name. */
    protected String name;
    
    /** The price. */
    protected double price;
    
    /** The vat. */
    protected double vat;

    /**
     * Gets the id.
     *
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * Sets the id.
     *
     * @param id the new id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the name.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the name.
     *
     * @param name the new name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets the price.
     *
     * @return the price
     */
    public double getPrice() {
        return price;
    }

    /**
     * Sets the price.
     *
     * @param price the new price
     */
    public void setPrice(double price) {
        this.price = price;
    }

    /**
     * Gets the vat.
     *
     * @return the vat
     */
    public double getVat() {
        return vat;
    }

    /**
     * Sets the vat.
     *
     * @param vat the new vat
     */
    public void setVat(double vat) {
        this.vat = vat;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("\t(").append(id).append(")\t").append(name).append("\t").append(price).append("\u20ac\tvat ").append(vat).append("%");
        return sb.toString();
    }
}
PdfInvoicesBasic.java
/*
 * This example was written by Bruno Lowagie in the context of a book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/5-creating-pdf-invoices-basic-profile
 */
package com.itextpdf.zugferd;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
import org.xml.sax.SAXException;

import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfOutputIntent;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.licensekey.LicenseKey;
import com.itextpdf.zugferd.data.InvoiceData;
import com.itextpdf.zugferd.exceptions.DataIncompleteException;
import com.itextpdf.zugferd.exceptions.InvalidCodeException;
import com.itextpdf.zugferd.pojo.Invoice;
import com.itextpdf.zugferd.pojo.Item;
import com.itextpdf.zugferd.pojo.PojoFactory;
import com.itextpdf.zugferd.pojo.Product;
import com.itextpdf.zugferd.profiles.IBasicProfile;

/**
 * Reads invoice data from a test database and creates ZUGFeRD invoices
 * (Basic profile).
 * @author Bruno Lowagie
 */
public class PdfInvoicesBasic {
    
    /** The pattern of the destination paths. */
    public static final String DEST = "results/zugferd/pdf/basic%05d.pdf";
    
    /** The path to the color profile. */
    public static final String ICC = "resources/color/sRGB_CS_profile.icm";
    
    /** The path to a regular font. */
    public static final String REGULAR = "resources/fonts/OpenSans-Regular.ttf";
    
    /** The path to a bold font. */
    public static final String BOLD = "resources/fonts/OpenSans-Bold.ttf";
    
    /** A String with a newline character. */
    public static final String NEWLINE = "\n";
    
    /**
     * The main method.
     *
     * @param args the arguments
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParserConfigurationException the parser configuration exception
     * @throws SQLException the SQL exception
     * @throws SAXException the SAX exception
     * @throws TransformerException the transformer exception
     * @throws ParseException the parse exception
     * @throws DataIncompleteException the data incomplete exception
     * @throws InvalidCodeException the invalid code exception
     */
    public static void main(String[] args) throws IOException, ParserConfigurationException, SQLException, SAXException, TransformerException, ParseException, DataIncompleteException, InvalidCodeException {
    	LicenseKey.loadLicenseFile(System.getenv("ITEXT7_LICENSEKEY") + "/itextkey-html2pdf_typography.xml");
    	File file = new File(DEST);
        file.getParentFile().mkdirs();
        PdfInvoicesBasic app = new PdfInvoicesBasic();
        PojoFactory factory = PojoFactory.getInstance();
        List invoices = factory.getInvoices();
        for (Invoice invoice : invoices) {
            app.createPdf(invoice);
        }
        factory.close();
    }
    
    /**
     * Creates a PDF file, given a certain invoice.
     *
     * @param invoice the invoice
     * @throws ParserConfigurationException the parser configuration exception
     * @throws SAXException the SAX exception
     * @throws TransformerException the transformer exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     * @throws DataIncompleteException the data incomplete exception
     * @throws InvalidCodeException the invalid code exception
     */
    public void createPdf(Invoice invoice) throws ParserConfigurationException, SAXException, TransformerException, IOException, ParseException, DataIncompleteException, InvalidCodeException {
        
    	String dest = String.format(DEST, invoice.getId());
    	
    	// Create the XML
        InvoiceData invoiceData = new InvoiceData();
        IBasicProfile basic = invoiceData.createBasicProfileData(invoice);
        InvoiceDOM dom = new InvoiceDOM(basic);
        
        // Create the ZUGFeRD document
    	ZugferdDocument pdfDocument = new ZugferdDocument(
    			new PdfWriter(dest), ZugferdConformanceLevel.ZUGFeRDBasic,
    			new PdfOutputIntent("Custom", "", "http://www.color.org",
        	            "sRGB IEC61966-2.1", new FileInputStream(ICC)));
        pdfDocument.addFileAttachment("ZUGFeRD invoice", PdfFileSpec.createEmbeddedFileSpec(
                pdfDocument, dom.toXML(), "ZUGFeRD invoice", "ZUGFeRD-invoice.xml",
                PdfName.ApplicationXml, new PdfDictionary(), PdfName.Alternative));
        
        // Create the document
    	Document document = new Document(pdfDocument);
        document.setFont(PdfFontFactory.createFont(REGULAR, true))
    			.setFontSize(12);
    	PdfFont bold = PdfFontFactory.createFont(BOLD, true);
    	
        // Add the header
    	document.add(
    		new Paragraph()
			.	setTextAlignment(TextAlignment.RIGHT)
				.setMultipliedLeading(1)
    			.add(new Text(String.format("%s %s\n", basic.getName(), basic.getId()))
    					.setFont(bold).setFontSize(14))
    			.add(convertDate(basic.getDateTime(), "MMM dd, yyyy")));
        // Add the seller and buyer address
        document.add(getAddressTable(basic, bold));
        // Add the line items
        document.add(getLineItemTable(invoice, bold));
        // Add the grand totals
        document.add(getTotalsTable(
                basic.getTaxBasisTotalAmount(), basic.getTaxTotalAmount(), basic.getGrandTotalAmount(), basic.getGrandTotalAmountCurrencyID(),
                basic.getTaxTypeCode(), basic.getTaxApplicablePercent(),
                basic.getTaxBasisAmount(), basic.getTaxCalculatedAmount(), basic.getTaxCalculatedAmountCurrencyID(), bold));
        // Add the payment info
        document.add(getPaymentInfo(basic.getPaymentReference(), basic.getPaymentMeansPayeeFinancialInstitutionBIC(), basic.getPaymentMeansPayeeAccountIBAN()));
        
        document.close();
    }

    /**
     * Convert a date to a String in a certain format.
     *
     * @param d the date
     * @param newFormat the new format
     * @return the date as a string
     * @throws ParseException the parse exception
     */
    public String convertDate(Date d, String newFormat) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(newFormat);
        return sdf.format(d);
    }
    
    /**
     * Gets the address table.
     *
     * @param basic the {@link IBasicProfile} instance
     * @param bold a bold font
     * @return the address table
     */
    public Table getAddressTable(IBasicProfile basic, PdfFont bold) {
        Table table = new Table(new UnitValue[]{
        		new UnitValue(UnitValue.PERCENT, 50),
        		new UnitValue(UnitValue.PERCENT, 50)})
        		.setWidth(UnitValue.createPercentValue(100));
        table.addCell(getPartyAddress("From:",
                basic.getSellerName(),
                basic.getSellerLineOne(),
                basic.getSellerLineTwo(),
                basic.getSellerCountryID(),
                basic.getSellerPostcode(),
                basic.getSellerCityName(),
                bold));
        table.addCell(getPartyAddress("To:",
                basic.getBuyerName(),
                basic.getBuyerLineOne(),
                basic.getBuyerLineTwo(),
                basic.getBuyerCountryID(),
                basic.getBuyerPostcode(),
                basic.getBuyerCityName(),
                bold));
        table.addCell(getPartyTax(basic.getSellerTaxRegistrationID(),
                basic.getSellerTaxRegistrationSchemeID(), bold));
        table.addCell(getPartyTax(basic.getBuyerTaxRegistrationID(),
                basic.getBuyerTaxRegistrationSchemeID(), bold));
        return table;
    }
    
    /**
     * Gets the party address.
     *
     * @param who either "To:" or "From:"
     * @param name the addressee
     * @param line1 line 1 of he address
     * @param line2 line 2 of the address
     * @param countryID the country ID
     * @param postcode the post code
     * @param city the city
     * @param bold a bold font
     * @return a formatted address cell
     */
    public Cell getPartyAddress(String who, String name, String line1, String line2, String countryID, String postcode, String city, PdfFont bold) {
    	Paragraph p = new Paragraph()
    			.setMultipliedLeading(1.0f)
        		.add(new Text(who).setFont(bold)).add(NEWLINE)
                .add(name).add(NEWLINE)
                .add(line1).add(NEWLINE)
                .add(line2).add(NEWLINE)
                .add(String.format("%s-%s %s", countryID, postcode, city));
        Cell cell = new Cell()
        		.setBorder(Border.NO_BORDER)
        		.add(p);
        return cell;
    }
    
    /**
     * Gets the party tax.
     *
     * @param taxId the tax id
     * @param taxSchema the tax schema
     * @param bold a bold font
     * @return a formatted cell
     */
    public Cell getPartyTax(String[] taxId, String[] taxSchema, PdfFont bold) {
    	Paragraph p = new Paragraph()
    		.setFontSize(10).setMultipliedLeading(1.0f)
			.add(new Text("Tax ID(s):").setFont(bold));
        if (taxId.length == 0) {
            p.add("\nNot applicable");
        }
        else {
            int n = taxId.length;
            for (int i = 0; i < n; i++) {
                p.add(NEWLINE)
                .add(String.format("%s: %s", taxSchema[i], taxId[i]));
            }
        }
        return new Cell().setBorder(Border.NO_BORDER).add(p);
    }
    
    /**
     * Gets the line item table.
     *
     * @param invoice the invoice
     * @param bold a bold font
     * @return the line item table
     */
    public Table getLineItemTable(Invoice invoice, PdfFont bold) {
        Table table = new Table(new UnitValue[]{
        		new UnitValue(UnitValue.PERCENT, 43.75f),
        		new UnitValue(UnitValue.PERCENT, 12.5f),
        		new UnitValue(UnitValue.PERCENT, 6.25f),
        		new UnitValue(UnitValue.PERCENT, 12.5f),
        		new UnitValue(UnitValue.PERCENT, 12.5f),
        		new UnitValue(UnitValue.PERCENT, 12.5f)})
        		.setWidth(UnitValue.createPercentValue(100))
				.setMarginTop(10).setMarginBottom(10);
        table.addHeaderCell(createCell("Item:", bold));
        table.addHeaderCell(createCell("Price:", bold));
        table.addHeaderCell(createCell("Qty:", bold));
        table.addHeaderCell(createCell("Subtotal:", bold));
        table.addHeaderCell(createCell("VAT:", bold));
        table.addHeaderCell(createCell("Total:", bold));
        Product product;
        for (Item item : invoice.getItems()) {
            product = item.getProduct();
            table.addCell(createCell(product.getName()));
            table.addCell(createCell(
            	InvoiceData.format2dec(InvoiceData.round(product.getPrice())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(String.valueOf(item.getQuantity()))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(item.getCost())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(product.getVat())))
                .setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(
                InvoiceData.format2dec(InvoiceData.round(
                	item.getCost() + ((item.getCost() * product.getVat()) / 100))))
                .setTextAlignment(TextAlignment.RIGHT));
        }
        return table;
    }
    
    /**
     * Creates a cell with specific properties set.
     *
     * @param text the text that will be in the cell
     * @return the cell
     */
    
    public Cell createCell(String text) {
    	return new Cell().setPadding(0.8f)
    		.add(new Paragraph(text)
    			.setMultipliedLeading(1));
    }
    
    /**
     * Creates a cell with specific properties set.
     *
     * @param text the text that will be in the cell
     * @param font the font
     * @return the cell
     */
    public Cell createCell(String text, PdfFont font) {
    	return new Cell().setPadding(0.8f)
        	.add(new Paragraph(text)
        		.setFont(font).setMultipliedLeading(1));
    }
    
    /**
     * Gets the totals table.
     *
     * @param tBase the total tax base
     * @param tTax the total tax amount
     * @param tTotal the total tax
     * @param tCurrency the tax currency
     * @param type the tax types
     * @param percentage the tax percentages
     * @param base the base amounts
     * @param tax the tax amounts
     * @param currency the currencies
     * @param bold a bold font
     * @return the totals table
     */
    public Table getTotalsTable(String tBase, String tTax, String tTotal, String tCurrency,
            String[] type, String[] percentage, String base[], String tax[], String currency[],
            PdfFont bold) {
        Table table = new Table(new UnitValue[]{
        		new UnitValue(UnitValue.PERCENT, 8.33f),
        		new UnitValue(UnitValue.PERCENT, 8.33f),
        		new UnitValue(UnitValue.PERCENT, 25f),
        		new UnitValue(UnitValue.PERCENT, 25f),
        		new UnitValue(UnitValue.PERCENT, 25f),
        		new UnitValue(UnitValue.PERCENT, 8.34f)})
        	.setWidth(UnitValue.createPercentValue(100));
        table.addCell(createCell("TAX:", bold));
        table.addCell(createCell("%", bold)
        	.setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell("Base amount:", bold));
        table.addCell(createCell("Tax amount:", bold));
        table.addCell(createCell("Total:", bold));
        table.addCell(createCell("Curr.:", bold));
        int n = type.length;
        for (int i = 0; i < n; i++) {
            table.addCell(createCell(type[i])
            	.setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(percentage[i])
            	.setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(base[i])
            	.setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(tax[i])
            	.setTextAlignment(TextAlignment.RIGHT));
            double total = Double.parseDouble(base[i]) + Double.parseDouble(tax[i]);
            table.addCell(createCell(
            	InvoiceData.format2dec(InvoiceData.round(total)))
            	.setTextAlignment(TextAlignment.RIGHT));
            table.addCell(createCell(currency[i]));
        }
        table.addCell(new Cell(1, 2).setBorder(Border.NO_BORDER));
        table.addCell(createCell(tBase, bold)
        	.setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tTax, bold)
        	.setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tTotal, bold)
        	.setTextAlignment(TextAlignment.RIGHT));
        table.addCell(createCell(tCurrency, bold));
        return table;
    }
    
    /**
     * Gets the payment info.
     *
     * @param ref the reference
     * @param bic the BIC code
     * @param iban the IBAN code
     * @return the payment info
     */
    public Paragraph getPaymentInfo(String ref, String[] bic, String[] iban) {
        Paragraph p = new Paragraph(String.format(
                "Please wire the amount due to our bank account using the following reference: %s",
                ref));
        int n = bic.length;
        for (int i = 0; i < n; i++) {
            p.add(NEWLINE).add(String.format("BIC: %s - IBAN: %s", bic[i], iban[i]));
        }
        return p;
    }
}
InvoiceData.java
/*
 * Code written by Bruno Lowagie in the context of an example for the ZUGFeRD book.
 * See http://developers.itextpdf.com/content/zugferd-future-invoicing/
 */
package com.itextpdf.zugferd.data;

import java.util.Map;
import java.util.TreeMap;

import com.itextpdf.zugferd.pojo.Customer;
import com.itextpdf.zugferd.pojo.Invoice;
import com.itextpdf.zugferd.pojo.Item;
import com.itextpdf.zugferd.profiles.BasicProfileImp;
import com.itextpdf.zugferd.profiles.ComfortProfileImp;
import com.itextpdf.zugferd.profiles.IBasicProfile;
import com.itextpdf.zugferd.profiles.IComfortProfile;
import com.itextpdf.zugferd.validation.basic.DateFormatCode;
import com.itextpdf.zugferd.validation.basic.DocumentTypeCode;
import com.itextpdf.zugferd.validation.basic.TaxIDTypeCode;
import com.itextpdf.zugferd.validation.basic.TaxTypeCode;
import com.itextpdf.zugferd.validation.comfort.FreeTextSubjectCode;
import com.itextpdf.zugferd.validation.comfort.PaymentMeansCode;
import com.itextpdf.zugferd.validation.comfort.TaxCategoryCode;

/**
 * Class in which we can store all the data of an {@link Invoice} object.
 *
 * @author Bruno Lowagie (iText Software)
 */
public class InvoiceData {
    
    /**
     * Creates a new {@link InvoiceData} instance.
     */
    public InvoiceData() {
    }
    
    /**
     * Creates a object that implements the {@link IBasicProfile} interface,
     * given a specific {@link Invoice} object.
     *
     * @param invoice the invoice
     * @return the {@link IBasicProfile} implementation
     */
    public IBasicProfile createBasicProfileData(Invoice invoice) {
    	BasicProfileImp profileImp = new BasicProfileImp(true);
        importData(profileImp, invoice);
        importBasicData(profileImp, invoice);
        return profileImp;
    }
    
    /**
     * Creates a object that implements the {@link IComfortProfile} interface,
     * given a specific {@link Invoice} object.
     *
     * @param invoice the invoice
     * @return the {@link IComfortProfile} implementation
     */
    public IComfortProfile createComfortProfileData(Invoice invoice) {
        ComfortProfileImp profileImp = new ComfortProfileImp(true);
        importData(profileImp, invoice);
        importComfortData(profileImp, invoice);
        return profileImp;
    }
   
    /**
     * Imports the data from an invoice into a {@link BasicProfileImp} instance.
     *
     * @param profileImp the {IBasicProfile} implementation
     * @param invoice the invoice
     */
    public void importData(BasicProfileImp profileImp, Invoice invoice) {
        profileImp.setTest(true);
        profileImp.setId(String.format("I/%05d", invoice.getId()));
        profileImp.setName("INVOICE");
        profileImp.setTypeCode(DocumentTypeCode.COMMERCIAL_INVOICE);
        profileImp.setDate(invoice.getInvoiceDate(), DateFormatCode.YYYYMMDD);
        profileImp.setSellerName("Das Company");
        profileImp.setSellerLineOne("ZUG Business Center");
        profileImp.setSellerLineTwo("Highway 1");
        profileImp.setSellerPostcode("9000");
        profileImp.setSellerCityName("Ghent");
        profileImp.setSellerCountryID("BE");
        profileImp.addSellerTaxRegistration(TaxIDTypeCode.FISCAL_NUMBER, "201/113/40209");
        profileImp.addSellerTaxRegistration(TaxIDTypeCode.VAT, "BE123456789");
        Customer customer = invoice.getCustomer();
        profileImp.setBuyerName(String.format("%s, %s", customer.getLastName(), customer.getFirstName()));
        profileImp.setBuyerPostcode(customer.getPostalcode());
        profileImp.setBuyerLineOne(customer.getStreet());
        profileImp.setBuyerLineTwo("");
        profileImp.setBuyerCityName(customer.getCity());
        profileImp.setBuyerCountryID(customer.getCountryId());
        profileImp.setPaymentReference(String.format("%09d", invoice.getId()));
        profileImp.setInvoiceCurrencyCode("EUR");
    }
    
    /**
     * Import the basic data into a {@link BasicProfileImp} instance.
     *
     * @param profileImp the {IBasicProfile} implementation
     * @param invoice the invoice
     */
    public void importBasicData(BasicProfileImp profileImp, Invoice invoice) {
        profileImp.addNote(
            new String[]{"This is a test invoice.\nNothing on this invoice is real.\nThis invoice is part of a tutorial."});
        profileImp.addPaymentMeans("", "", "BE 41 7360 0661 9710", "", "", "KREDBEBB", "", "KBC");
        profileImp.addPaymentMeans("", "", "BE 56 0015 4298 7888", "", "", "GEBABEBB", "", "BNP Paribas");
        Map taxes = new TreeMap();
        double tax;
        for (Item item : invoice.getItems()) {
            tax = item.getProduct().getVat();
            if (taxes.containsKey(tax)) {
                taxes.put(tax, taxes.get(tax) + item.getCost());
            }
            else {
                taxes.put(tax, item.getCost());
            }
            profileImp.addIncludedSupplyChainTradeLineItem(format4dec(item.getQuantity()), "C62", item.getProduct().getName());
        }
        double total, tA;
        double ltN = 0;
        double ttA = 0;
        double gtA = 0;
        for (Map.Entry t : taxes.entrySet()) {
            tax = t.getKey();
            total = round(t.getValue());
            gtA += total;
            tA = round((100 * total) / (100 + tax));
            ttA += (total - tA);
            ltN += tA;
            profileImp.addApplicableTradeTax(format2dec(total - tA), "EUR", TaxTypeCode.VALUE_ADDED_TAX, format2dec(tA), "EUR", format2dec(tax));
        }
        profileImp.setMonetarySummation(format2dec(ltN), "EUR",
            format2dec(0), "EUR",
            format2dec(0), "EUR",
            format2dec(ltN), "EUR",
            format2dec(ttA), "EUR",
            format2dec(gtA), "EUR");
    }
   
    /**
     * Import comfort data into a {@link ComfortProfileImp} instance.
     *
     * @param profileImp the {IComfortProfile} implementation
     * @param invoice the invoice
     */
    public void importComfortData(ComfortProfileImp profileImp, Invoice invoice) {
        profileImp.addNote(
            new String[]{"This is a test invoice.\nNothing on this invoice is real.\nThis invoice is part of a tutorial."},
            FreeTextSubjectCode.REGULATORY_INFORMATION);
        profileImp.addPaymentMeans(
                PaymentMeansCode.PAYMENT_TO_BANK_ACCOUNT,
                new String[]{"This is the preferred bank account."},
                "", "",
                "", "",
                "BE 41 7360 0661 9710", "", "",
                "", "", "",
                "KREDBEBB", "", "KBC");
        profileImp.addPaymentMeans(
                PaymentMeansCode.PAYMENT_TO_BANK_ACCOUNT,
                new String[]{"Use this as an alternative account."},
                "", "",
                "", "",
                "BE 56 0015 4298 7888", "", "",
                "", "", "",
                "GEBABEBB", "", "BNP Paribas");
        Map taxes = new TreeMap();
        double tax;
        int counter = 0;
        for (Item item : invoice.getItems()) {
            counter++;
            tax = item.getProduct().getVat();
            if (taxes.containsKey(tax)) {
                taxes.put(tax, taxes.get(tax) + item.getCost());
            }
            else {
                taxes.put(tax, item.getCost());
            }
            profileImp.addIncludedSupplyChainTradeLineItem(
                    String.valueOf(counter),
                    null,
                    format4dec(item.getProduct().getPrice()), "EUR", null, null,
                    null, null, null, null,
                    null, null, null, null,
                    format4dec(item.getQuantity()), "C62",
                    new String[]{TaxTypeCode.VALUE_ADDED_TAX},
                    new String[1],
                    new String[]{TaxCategoryCode.STANDARD_RATE},
                    new String[]{format2dec(item.getProduct().getVat())},
                    format2dec(item.getCost()), "EUR",
                    null, null,
                    String.valueOf(item.getProduct().getId()), null,
                    item.getProduct().getName(), null
            );
        }
        double total, tA;
        double ltN = 0;
        double ttA = 0;
        double gtA = 0;
        for (Map.Entry t : taxes.entrySet()) {
            tax = t.getKey();
            total = round(t.getValue());
            gtA += total;
            tA = round((100 * total) / (100 + tax));
            ttA += (total - tA);
            ltN += tA;
            profileImp.addApplicableTradeTax(
                    format2dec(total - tA), "EUR", TaxTypeCode.VALUE_ADDED_TAX,
                    null, format2dec(tA), "EUR",
                    TaxCategoryCode.STANDARD_RATE, format2dec(tax));
        }
        profileImp.setMonetarySummation(format2dec(ltN), "EUR",
            format2dec(0), "EUR",
            format2dec(0), "EUR",
            format2dec(ltN), "EUR",
            format2dec(ttA), "EUR",
            format2dec(gtA), "EUR");
    }
    
    /**
     * Round a double value.
     *
     * @param d the double value
     * @return the rounded double
     */
    public static double round(double d) {
        d = d * 100;
        long tmp = Math.round(d);
        return (double) tmp / 100;
    }
    
    /**
     * Format a double so that it has 2 decimals.
     *
     * @param d the double value
     * @return a string representation of the double value
     */
    public static String format2dec(double d) {
        return String.format("%.2f", d);
    }
    
    /**
     * Format a double so that it has 4 decimals.
     *
     * @param d the double value
     * @return a string representation of the double value
     */
    public static String format4dec(double d) {
        return String.format("%.4f", d);
    }
    
}
Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now