vat : Adding VAT (Value-added tax) functionality

The lino_xl.lib.vat plug-in adds functionality for handling sales and purchase invoices in a context where the site operator is subject to value-added tax (VAT). It provides a framework for handling VAT declarations. When using this plugin, you will probably also install one of the national VAT implementations.

This is a tested document. The following instructions are used for initialization:

>>> from lino import startup
>>> startup('lino_book.projects.pierre.settings.doctests')
>>> from lino.api.doctest import *

National VAT implementations

Applications using this plugin must specify the national implementations for their VAT declarations by setting the declaration_plugin plugin attribute.

Currently we have three declaration plug-ins:

Accounting applications to be used by site operators who don't care about VAT might use lino_xl.lib.vatless instead (though this plugin might become deprecated). The modules lino_xl.lib.vatless and lino_xl.lib.vat can theoretically both be installed though obviously this wouldn't make sense.

VAT regimes

>>>, language="en")
======= ======== ======== ========== ==============
 value   name     text     VAT area   Needs VAT id
------- -------- -------- ---------- --------------
 10      normal   Normal              No
======= ======== ======== ========== ==============
class lino_xl.lib.vat.VatRegime

Base class for the items of VatRegimes. Each VAT regime is an instance of this and has two properties:


In which VAT area this regime is available.


Whether unit prices are VAT included or not. No longer used. See Plugin.item_vat instead.


Whether this VAT regime requires that partner to have a vat_id.

class lino_xl.lib.vat.VatRegimes

The global list of VAT regimes. Each item of this list is an instance of VatRegime.

Three VAT regimes are considered standard minimum:


Two additional regimes are defined in lino_xl.lib.bevat:


VAT classes

See also VAT classes.

>>>, language="en")
======= ============= ===========================
 value   name          text
------- ------------- ---------------------------
 010     goods         Goods at normal VAT rate
 020     reduced       Goods at reduced VAT rate
 030     exempt        Goods exempt from VAT
 100     services      Services
 200     investments   Investments
 210     real_estate   Real estate
 220     vehicles      Vehicles
 300     vatless       Operations without VAT
======= ============= ===========================
class lino_xl.lib.vat.VatClasses

The global list of VAT classes.

Default classes are:


VAT rules

When no national declaration module is installed, we have only one default VAT rule with no condition and zero rate.

>>>, language="en")
| value | Description      |
| 1     | VAT rule 1:      |
|       | apply 0 %        |
|       | and book to None |
class lino_xl.lib.vat.VatRule

A rule which defines how VAT is to be handled for a given invoice item.

Example data see lino_xl.lib.vat.fixtures.euvatrates.

Database fields:


The sequence number.


The regime for which this rule applies.

Pointer to VatRegimes.


The VAT rate to be applied. Note that a VAT rate of 20 percent is stored as 0.20 (not 20).


The general account where VAT is to be booked.


Whether VAT is returnable. Returnable VAT does not increase the total amount of the voucher, it causes an additional movement into the vat_returnable_account. See About returnable VAT.


Where to book returnable VAT. If this field is empty and vat_returnable is True, then VAT will be added to the base account. See About returnable VAT.

get_vat_rule(cls, trade_type, vat_regime,
vat_class=None, country=None, date=None)

Return the VAT rule to be applied for the given criteria.

Lino loops through all rules (ordered by their seqno) and returns the first object which matches.

class lino_xl.lib.vat.VatRules

The table of all VatRule objects.

This table is accessible via Explorer ‣ VAT ‣ VAT rules.

>>> show_menu_path(vat.VatRules, language='en')
Explorer --> VAT --> VAT rules

This table is filled by

VAT areas

The VatAreas choice list contains the list of available VAT areas.

>>>, language="en")
======= =============== ===============
 value   name            text
------- --------------- ---------------
 10      national        National
 20      eu              EU
 30      international   International
======= =============== ===============

The plugin property eu_country_codes defines which countries are considered part of the EU.

Available VAT regimes

The declaration plugin controls which VAT regimes are available for selection on a partner or on a voucher.

The available VAT regimes vary depending on which VAT declaration plugin is installed. When no declaration module is installed, we have only one default regime.

The list of available VAT regimes for a partner depends on the VAT area and on whether the partner has a VAT id or not.

get_vat_regime_choices(country=None, vat_id=None):

Used for the choosers of the vat_regime field of a partner and a voucher.

class lino_xl.lib.vat.VatAreas

The global list of VAT areas.

classmethod get_for_country(cls, country)

Return the VAT area for this country.

Why differentiate between VAT regimes and VAT classes?

You might ask why we use two sets of categories for specifying the VAT rate. Some other accounting programs do not have two different categories for the subtle difference between "exempt from VAT" and "VAT 0%", they have just a category "VAT rate" which you can set per invoice item (and a default value per provider).

The problem with this simplified vision is that at least for Belgian VAT declarations there is a big difference between having 0% of VAT because the provider is a private person and having 0% of VAT because you are buying post stamps or flight tickets (which are exempt from VAT).

Another thing to consider is that in Lino we want to be able to have partners who are both a provider and a customer. Their VAT regime remains the same for both trade types (sales and purchase) while the default VAT class to use in invoice items depends on the account or the product.

Account invoices

The lino_xl.lib.vat plugin provides the VatAccountInvoice voucher type. It is implemented in two database models:

class lino_xl.lib.vat.VatAccountInvoice

Django model for storing account vouchers.

A VAT-capable of account voucher. It is one of the most basic voucher types, which can be used even in accounting applications that don't have lino_xl.lib.sales.

class lino_xl.lib.vat.InvoiceItem

Django model for representing items of an account voucher.

There are several views:

class lino_xl.lib.vat.Invoices

The table of all VatAccountInvoice objects.

class lino_xl.lib.vat.InvoicesByJournal

Shows all invoices of a given journal (whose voucher_type must be VatAccountInvoice)

class lino_xl.lib.vat.PrintableInvoicesByJournal

Purchase journal

class lino_xl.lib.vat.InvoiceDetail

The detail layout used by Invoices.

class lino_xl.lib.vat.ItemsByInvoice
class lino_xl.lib.vat.VouchersByPartner


The lino_xl.lib.vat.utils module contains some utility functions.

>>> from lino_xl.lib.vat.utils import add_vat, remove_vat
>>> add_vat(100, 21)
>>> remove_vat(121, 21)
>>> add_vat(10, 21)
>>> add_vat(1, 21)

Showing the invoices covered by a VAT declaration

The plugin defines two tables that show the invoices covered by a VAT declaration, IOW the invoices that have contributed to the numbers in the declaration.

class lino_xl.lib.vat.SalesByDeclaration

Show a list of all sales invoices whose VAT regime is Intra-Community.

class lino_xl.lib.vat.PurchasesByDeclaration

Show a list of all purchase invoices whose VAT regime is Intra-Community.

class lino_xl.lib.vat.VatInvoices

Common base class for SalesByDeclaration and PurchasesByDeclaration

Intracom sales and purchases

The plugin defines two reports accessible via the Reports ‣ Accounting menu and integrated in the printout of a VAT declaration:

class lino_xl.lib.vat.IntracomSales

Show a list of all sales invoices whose VAT regime is Intra-Community.

class lino_xl.lib.vat.IntracomPurchases

Show a list of all purchase invoices whose VAT regime is Intra-Community.

class lino_xl.lib.vat.IntracomInvoices

Common base class for IntracomSales and IntracomPurchases

These reports are empty when you have no national declaration plugin installed:

>>>, language='en')
No data to display
>>>, language='en')
No data to display

Model mixins

class lino_xl.lib.vat.VatTotal

Model mixin which defines the database fields total_incl, total_base and total_vat and some related behaviour.

Used for both the voucher (VatDocument) and for each item (VatItemBase).


The amount VAT included.


The amount VAT excluded.


The amount of VAT.

All three total fields are lino.core.fields.PriceField instances.

The fields are editable by default, but implementing models can call lino.core.fields.update_field() to change this behaviour. A model that sets all fields to non-editable should also set edit_totals to False.


Subclasses of VatTotal must implement this method.


Return the VAT rule for this voucher or voucher item. Called when user edits a total field in the document header when edit_totals is True.


Called when user has edited the total_base field. If total_base has been set to blank, then Lino fills it using reset_totals(). If user has entered a value, compute total_vat and total_incl from this value using the vat rate. If there is no VatRule, total_incl and total_vat are set to None.

If there are rounding differences, total_vat will get them.


Called when user has edited the total_vat field. If it has been set to blank, then Lino fills it using reset_totals(). If user has entered a value, compute total_incl. If there is no VatRule, total_incl is set to None.


Called when user has edited the total_incl field. If total_incl has been set to blank, then Lino fills it using reset_totals(). If user enters a value, compute total_base and total_vat from this value using the vat rate. If there is no VatRule, total_incl should be disabled, so this method will never be called.

If there are rounding differences, total_vat will get them.

class lino_xl.lib.vat.VatDocument

Abstract base class for invoices, offers and other vouchers.

Inherited by VatAccountInvoice as well as in other plugins (e.g. lino_xl.lib.sales.VatProductInvoice and lino_xl.lib.ana.AnaAccountInvoice).

Models that inherit this mixin can set the following class attribute:


Whether the user usually wants to edit the total amount or not.

The total fields of an invoice are not automatically updated each time an item is modified. Users must click the Σ ("Compute sums") button (or Save or the Register button) to see the invoice's totals.

Inherits the following database fields from VatTotal:


Adds the following database fields:


Pointer to a lino_xl.lib.ledger.Plugin.project_model.


Mandatory field to be defined in the implementing class.


An automatically managed boolean field which says whether the user has manually edited the items of this document. If this is False and edit_totals is True, Lino will automatically update the only invoice item according to partner and vat_regime and total_incl.


The VAT regime to be used in this document.

A pointer to VatRegimes.

Adds an action:


Calls ComputeSums for this document.

class lino_xl.lib.vat.ComputeSums

Compute the sum fields of a VatDocument based on its items.

Represented by a "Σ" button.

class lino_xl.lib.vat.VatItemBase

Model mixin for items of a VatDocument.

Abstract Base class for lino_xl.lib.ledger.InvoiceItem, i.e. the lines of invoices without unit prices and quantities.

Subclasses must define a field called "voucher" which must be a ForeignKey with related_name="items" to the "owning document", which in turn must be a subclass of VatDocument).


The VAT class to be applied for this item. A pointer to VatClasses.

get_vat_rule(self, tt)

Return the VatRule which applies for this item.

tt is the trade type (which is the same for each item of a voucher, that's why we expect the caller to provide it).

This basically calls the class method VatRule.get_vat_rule() with appropriate arguments.

When selling certain products ("automated digital services") in the EU, you have to pay VAT in the buyer's country at that country's VAT rate. See e.g. How can I comply with VAT obligations?.

TODO: Add a new attribute VatClass.buyers_country or a checkbox Product.buyers_country or some other way to specify this.

class lino_xl.lib.vat.QtyVatItemBase

Model mixin for items of a VatTotal. Extends VatItemBase by adding unit_price and qty.

Abstract Base class for lino_xl.lib.sales.InvoiceItem and lino_xl.lib.sales.OrderItem, i.e. invoice items with unit prices and quantities.


The unit price for this item.


Changing the unit_price ot the qty will automatically reset the total amount of this item: the value unit_price * qty will be stored in total_incl if VatRegime.item_vat is True, otherwise in total_base.

VAT columns

class lino_xl.lib.vat.VatColumns

The global list of VAT columns.

The VAT column of a ledger account indicates where the movements on this account are to be collected in VAT declarations.

VAT declarations

A VAT declaration is a voucher that expresses that a corporation declares to its government how much sales and purchases they've done during a given period.

A VAT declaration is a computed summary of ledger movements in an observed period or range of periods. The voucher itself is a ledger voucher that generates new movements in its own period of declration, which is different from the observed period range.

class lino_xl.lib.vat.VatDeclaration

Abstract base class for VAT declarations.

Inherits from lino_xl.lib.sepa.Payable lino_xl.lib.ledger.Voucher lino_xl.lib.excerpts.Certifiable lino_xl.lib.ledger.PeriodRange


Implements lino_xl.lib.sepa.Payable.get_payable_sums_dict().

As a side effect this updates values in the computed fields of this declaration.

Declaration fields

Defining the declaration fields is responsibility of each national implementation plugin. But every individual field in every VAT declaration of every country is an instance of one of the following three classes:

class lino_xl.lib.vat.MvtDeclarationField

A declaration field to be computed by analyzing the ledger movements.

class lino_xl.lib.vat.WritableDeclarationField

A declaration field to be entered manually by the end user.

class lino_xl.lib.vat.SumDeclarationField

A declaration field that computes the sum of its observed fields.

All these three declaration field classes have a common ancestor DeclarationField.

class lino_xl.lib.vat.DeclarationField

Base class for all declaration fields.

It is not instantiated directly but by using one of its subclasses


Whether the value of this field is to be manually entered by the end user.

Most fields are not editable, i.e. computed.


Whether the value of this field is to be manually entered by the end user.


An optional space-separated list of names of observed fields, i.e. other declaration fields to be observed by this field. If a field name is prefixed by a "-", the observed field will additionally be inverted.

This is used only by sum fields. The values of all observed fields will be added, except inverted fields whose value will be subtracted.

Note that the booking direction (D or C) of the observed fields is ignored when computing the sum.


Whether the value of this field represents an amount to be paid to the tax office.

class lino_xl.lib.vat.DeclarationFieldsBase


See also lino_xl.lib.vat.Plugin for configuration options.

Fill invoice items based on voucher's total

In a VatAccountInvoice, end users may edit the total amount of the invoice in order to have Lino assist them for filling invoice items based on this amount.

Manually editing the VAT amount of invoice items

End users can manually edit any amount of an invoice item.

When you enter a total_incl, Lino automatically computes the total_base and total_vat. But when entering data from a legacy system, you may want to manually specify a different VAT amount.


An electricity invoice of 94,88 €. Only 35% of the total amount is deductible.

  • Manually enter 94.88 in VatProductInvoice.total_incl. Lino fills one invoice item. The general account of this item is either the provider's purchase_account or (if that field is empty) lino_xl.lib.ledger.CommonAccounts.waiting.

  • Change the amount of the invoice item (total_incl) from 94.88 to 33.22 (94.88 * 0.35). Lino automatically sets total_base to 27.68 € (33.22 / 1.20) and total_vat to 5.54 (33.22 - 27.68).

  • Add a second line and manually set InvoiceItem.account to 600020 (Non deducible costs). Lino automatically fills the remaining amount (94.88 - 33.22 = 61.66) into the InvoiceItem.total_incl field and computes the other amounts of that line. Since account 600020 has vat_class set to exempt, the other amounts are set to blank.

About returnable VAT

The vat_returnable_account attribute tells Lino whether this is considered returnable VAT.

The VAT columns checker

class lino_xl.lib.vat.VatColumnsChecker

Check VAT columns configuration.

This is an unbound data checker (lino.modlib.checkdata.Checker.model is None), i.e. the messages aren't bound to a particular database object.