Accounting localization
Warning
This tutorial requires knowledge about how to build a module in Odoo (see Building a Module).
Installation procedure
On installing the account module, the localization module corresponding to the country code of the company is installed automatically. In case of no country code set or no localization module found, the l10n_generic_coa (US) localization module is installed by default. Check post init hook for details.
For example, l10n_ch will be installed if the company has Switzerland
as country.
Building a localization module
The structure of a basic l10n_XX
module may be described with the following __manifest__.py
file:
{
"name": "COUNTRY - Accounting",
"version": "1.0.0",
"category": "Accounting/Localizations/Account Charts",
"license": "LGPL-3",
"depends": [
"account",
# "l10n_multilang",
],
"data": [
# Chart of Accounts
"data/account_chart_template_data.xml",
"data/account_account_tag_data.xml",
"data/account.account.template.csv",
"data/account.group.template.csv",
# Taxes
"data/account_tax_group_data.xml",
"data/account_tax_report_data.xml",
"data/account_tax_template_data.xml",
"data/account_fiscal_position_template_data.xml",
"data/account_account_template_post_data.xml",
"data/account_chart_post_data.xml",
"data/account_chart_template_try_loading.xml",
# Views and others
"views/xxxmodel_views.xml"
],
"demo": [
"demo/demo_company.xml",
]
}
In the first file data/account_chart_template_data.xml
, we set the name for the chart of accounts along with some basic fields.
See also
Example
addons/l10n_ch/data/l10n_ch_chart_data.xml
<record id="l10nch_chart_template" model="account.chart.template">
<field name="name">Plan comptable 2015 (Suisse)</field>
<field name="code_digits">4</field>
<field name="bank_account_code_prefix">102</field>
<field name="cash_account_code_prefix">100</field>
<field name="transfer_account_code_prefix">1090</field>
<field name="currency_id" ref="base.CHF"/>
<field name="country_id" ref="base.ch"/>
<field name="spoken_languages" eval="'it_IT;de_DE;de_CH;fr_FR;fr_CH'"/>
</record>
Note
Recommended xmlid for the record is chart_template
. If you need many chart of accounts, you can add some suffixes, i.e. chart_template_XXX
.
Chart of Accounts
Accounts
Obviously, Chart of Accounts cannot exist without Accounts. You need to specify them in data/account.account.template.csv
.
Example
addons/l10n_ch/data/account.account.template.csv
"id","name","code","account_type","chart_template_id/id","reconcile"
"ch_coa_1060","Securities (with stock exchange price)","1060","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1069","Accumulated depreciation on securities","1069","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1091","Transfer account: Salaries","1091","asset_current","l10n_ch.l10nch_chart_template","True"
"ch_coa_1099","Transfer account: miscellaneous","1099","asset_current","l10n_ch.l10nch_chart_template","True"
"ch_coa_1100","Accounts receivable from goods and services (Debtors)","1100","asset_receivable","l10n_ch.l10nch_chart_template","True"
"ch_coa_1101","Receivable (PoS)","1101","asset_receivable","l10n_ch.l10nch_chart_template","True"
"ch_coa_1109","Del credere (Acc. depr. on debtors)","1109","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1140","Advances and loans","1140","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1149","Advances and loans adjustments","1149","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1170","Input Tax (VAT) receivable on material, goods, services, energy","1170","asset_current","l10n_ch.l10nch_chart_template","False"
"ch_coa_1171","Input Tax (VAT) receivable on investments, other operating expenses","1171","asset_current","l10n_ch.l10nch_chart_template","False"
CSV is prefered but you may use XML format instead.
Example
addons/l10n_at/data/account_account_template.xml
<record id="chart_at_template_0010" model="account.account.template">
<field name="name">Aufwendungen für das Ingangsetzen und Erweitern eines Betriebes</field>
<field name="code">0010</field>
<field name="reconcile" eval="False"/>
<field name="chart_template_id" ref="l10n_at_chart_template"/>
<field name="account_type">asset_non_current</field>
<field name="tag_ids" eval="[(6, 0, [ref('l10n_at.account_tag_l10n_at_AAI1')])]" />
</record>
Warning
Avoid the usage of liquidity
account.account.type
! Indeed, the bank & cash accounts are created directly at the installation of the localization module and then, are linked to anaccount.journal
.Only one account of type payable/receivable is enough for the generic case. We need to define a PoS receivable account as well however. (linked in the CoA)
Don’t create too many accounts: 200-300 is enough. But mostly, we try to find a good balance where the CoA needs minimal adapting for most companies afterwards.
Next settings for the chart of accounts are set in a separate file, because we need to provide a list of accounts first. In data/account_chart_post_data.xml
, we set some default accounts:
Example
addons/l10n_ch/data/l10n_ch_chart_post_data.xml
<record id="l10nch_chart_template" model="account.chart.template">
<field name="property_account_receivable_id" ref="ch_coa_1100"/>
<field name="property_account_payable_id" ref="ch_coa_2000"/>
<field name="property_account_expense_categ_id" ref="ch_coa_4200"/>
<field name="property_account_income_categ_id" ref="ch_coa_3200"/>
<field name="income_currency_exchange_account_id" ref="ch_coa_3806"/>
<field name="expense_currency_exchange_account_id" ref="ch_coa_4906"/>
<field name="default_pos_receivable_account_id" ref="ch_coa_1101" />
<field name="account_journal_early_pay_discount_loss_account_id" ref="ch_coa_4901"/>
<field name="account_journal_early_pay_discount_gain_account_id" ref="ch_coa_3801"/>
</record>
Account groups
See also
Account groups allow describing the hierarchical structure of the chart of accounts. The filter needs to be activated in the report and then when you decollapse into journal entries it will show the parents of the account.
It works with the prefix start/end, so every account where the code starts with something between start and end will have this account.group as the parent group. Furthermore, the account groups can have a parent account group as well to form the hierarchy.
Example
addons/l10n_il/data/account.group.template.csv
id | code_prefix_start | code_prefix_end | name | chart_template_id/id |
---|---|---|---|---|
il_group_100100 | 100100 | 100499 | Fixed Assets | l10n_il.il_chart_template |
il_group_101110 | 101110 | 101400 | Current Assets | l10n_il.il_chart_template |
il_group_101401 | 101401 | 101799 | Bank And Cash | l10n_il.il_chart_template |
il_group_111000 | 111000 | 111999 | Current Liabilities | l10n_il.il_chart_template |
il_group_112000 | 112000 | 112210 | Non-current Liabilities | l10n_il.il_chart_template |
il_group_200000 | 200000 | 200199 | Sales Income | l10n_il.il_chart_template |
il_group_200200 | 200200 | 200300 | Other Income | l10n_il.il_chart_template |
il_group_201000 | 201000 | 201299 | Cost of Goods | l10n_il.il_chart_template |
il_group_202000 | 202000 | 220900 | Expenses | l10n_il.il_chart_template |
il_group_300000 | 300000 | 399999 | Capital And Shares | l10n_il.il_chart_template |
Taxes
See also
To add taxes you first need to specify tax groups. You normally need just one tax group for every tax rate, except for the 0% as you need to often distinguish between exempt, 0%, not subject, … taxes. This model only has two required fields: name and country. Create the file data/account_tax_group_data.xml
and list the groups:
<odoo>
<data noupdate="1">
<record id="tax_group_tva_0" model="account.tax.group">
<field name="name">TVA 0%</field>
<field name="country_id" ref="base.ch"/>
</record>
...
</data>
</odoo>
Example
addons/l10n_ch/data/account_tax_group_data.xml
<record id="tax_group_tva_0" model="account.tax.group">
<field name="name">TVA 0%</field>
<field name="country_id" ref="base.ch"/>
</record>
<record id="tax_group_tva_25" model="account.tax.group">
<field name="name">TVA 2.5%</field>
<field name="country_id" ref="base.ch"/>
</record>
<record id="tax_group_tva_37" model="account.tax.group">
<field name="name">TVA 3.7%</field>
<field name="country_id" ref="base.ch"/>
</record>
<record id="tax_group_tva_77" model="account.tax.group">
<field name="name">TVA 7.7%</field>
<field name="country_id" ref="base.ch"/>
</record>
<record id="tax_group_tva_100" model="account.tax.group">
<field name="name">TVA 100%</field>
<field name="country_id" ref="base.ch"/>
</record>
Example
addons/l10n_uk/data/account.tax.group.csv
id,name,country_id/id
tax_group_0,TAX 0%,base.uk
tax_group_5,TAX 5%,base.uk
tax_group_175,TAX 17.5%,base.uk
tax_group_20,TAX 20%,base.uk
Now you can add the taxes via data/account_tax_template_data.xml
file. The first tax you define that is purchase/sale also becomes the default purchase/sale tax for your products.
Example
addons/l10n_ae/data/account_tax_template_data.xml
<record id="uae_sale_tax_5_dubai" model="account.tax.template">
<field name="name">VAT 5% (Dubai)</field>
<field name="type_tax_use">sale</field>
<field name="amount">5</field>
<field name="amount_type">percent</field>
<field name="description">VAT 5%</field>
<field name="tax_group_id" ref="ae_tax_group_5"/>
<field name="chart_template_id" ref="uae_chart_template_standard"/>
<field name="invoice_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'repartition_type': 'base',
'plus_report_expression_ids': [ref('tax_report_line_standard_rated_supplies_base_dubai_tag')],
}),
(0,0, {
'repartition_type': 'tax',
'account_id': ref('uae_account_201017'),
'plus_report_expression_ids': [ref('tax_report_line_standard_rated_supplies_vat_dubai_tag')],
}),
]"/>
<field name="refund_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'repartition_type': 'base',
'minus_report_expression_ids': [ref('tax_report_line_standard_rated_supplies_base_dubai_tag')],
}),
(0,0, {
'repartition_type': 'tax',
'account_id': ref('uae_account_201017'),
'minus_report_expression_ids': [ref('tax_report_line_standard_rated_supplies_vat_dubai_tag')],
}),
]"/>
</record>
If some accounts should use default taxes, you can set them up in data/account_account_template_post_data.xml
Tax Report
The tax report is declared in the Invoicing (account
) app, but the report is only accessible when Accounting (account_accountant
) is installed.
In the previous section, you noticed the fields invoice_repartition_line_ids
or refund_repartition_line_ids
and probably understood nothing about them. Good news: you are not alone on this incomprehension. Bad news: you have to figure it out a bit. The topic is complicated. Indeed:
The simple version is that, in the tax template, you indicate in the invoice/refund repartition lines whether the base or a percentage of the tax needs to be reported in which report line (through the minus/plus_report_line_ids fields). It becomes clear also when you check the tax configuration in the Odoo interface (or check the docs Tax References, Tax Repartition References).
So, once you have properly configured taxes, you just need to add the data/account_tax_report_data.xml
file with a record for your account.report
. For it to be considered as a tax report, you need to provide it with the right root_report_id
.
<odoo>
<record id="tax_report" model="account.report">
<field name="name">Tax Report</field>
<field name="root_report_id" ref="account.generic_tax_report"/>
<field name="country_id" ref="base.XX"/>
</record>
...
</odoo>
… followed by the declaration of its lines, as account.report.line
records.
Example
addons/l10n_au/data/account_tax_report_data.xml
<record id="tax_report" model="account.report">
<field name="name">Tax Report</field>
<field name="root_report_id" ref="account.generic_tax_report"/>
<field name="country_id" ref="base.au"/>
<field name="filter_fiscal_position" eval="True"/>
<field name="availability_condition">country</field>
<field name="column_ids">
<record id="tax_report_balance" model="account.report.column">
<field name="name">Balance</field>
<field name="expression_label">balance</field>
</record>
</field>
<field name="line_ids">
<record id="account_tax_report_gstrpt_sale_total" model="account.report.line">
<field name="name">GST amounts you owe the Tax Office from sales</field>
<field name="children_ids">
<record id="account_tax_report_gstrpt_g1" model="account.report.line">
<field name="name">G1: Total Sales (including any GST)</field>
<field name="code">G1</field>
<field name="expression_ids">
<record id="account_tax_report_gstrpt_g1_tag" model="account.report.expression">
<field name="label">balance</field>
<field name="engine">tax_tags</field>
<field name="formula">G1</field>
</record>
</field>
<field name="children_ids">
<record id="account_tax_report_gstrpt_g2" model="account.report.line">
<field name="name">G2: Export sales</field>
<field name="code">G2</field>
<field name="expression_ids">
<record id="account_tax_report_gstrpt_g2_tag" model="account.report.expression">
<field name="label">balance</field>
<field name="engine">tax_tags</field>
<field name="formula">G2</field>
</record>
</field>
</record>
Fiscal positions
Specify fiscal positions in the data/account_fiscal_position_template_data.xml
file.
Example
addons/l10n_es/data/account_fiscal_position_template_data.xml
<record id="fp_nacional" model="account.fiscal.position.template">
<field name="sequence">1</field>
<field name="name">Régimen Nacional</field>
<field name="chart_template_id" ref="account_chart_template_common"/>
<field name="auto_apply" eval="True"/>
<field name="vat_required" eval="True"/>
<field name="country_id" ref="base.es"/>
</record>
Final steps
The last step when installing a localization module is to try to apply its chart of accounts to the current company (if it does not already have one). The file data/account_chart_template_try_loading.xml
is responsible for that.
Example
addons/l10n_ch/data/account_chart_template_data.xml
<function model="account.chart.template" name="try_loading">
<value eval="[ref('l10n_ch.l10nch_chart_template')]"/>
</function>
Finally, you may add a demo company, so the localization can easily be tested in demo mode.
Example
addons/l10n_ch/demo/demo_company.xml
<record id="partner_demo_company_ch" model="res.partner">
<field name="name">CH Company</field>
<field name="vat">CHE-530781296TVA</field>
<field name="street">14 Meierskappelerstrasse</field>
<field name="city">Risch-Rotkreuz</field>
<field name="country_id" ref="base.ch"/>
<field name="zip">6343</field>
<field name="phone">+41 78 123 45 67</field>
<field name="email">info@company.chexample.com</field>
<field name="website">www.chexample.com</field>
</record>
<record id="demo_company_ch" model="res.company">
<field name="name">CH Company</field>
<field name="partner_id" ref="partner_demo_company_ch"/>
</record>
<function model="res.company" name="_onchange_country_id">
<value eval="[ref('demo_company_ch')]"/>
</function>
<function model="res.users" name="write">
<value eval="[ref('base.user_root'), ref('base.user_admin'), ref('base.user_demo')]"/>
<value eval="{'company_ids': [(4, ref('l10n_ch.demo_company_ch'))]}"/>
</function>
<function model="account.chart.template" name="try_loading">
<value eval="[ref('l10n_ch.l10nch_chart_template')]"/>
<value model="res.company" eval="obj().env.ref('l10n_ch.demo_company_ch')"/>
</function>
Accounting reports
See also
Accounting reports should be added via a separate module l10n_XX_reports
that should go to the enterprise repository.
Basic __manifest__.py
file for such a module looks as following:
{
"name": "COUNTRY - Accounting Reports",
"category": "Accounting/Localizations/Reporting",
"version": "1.0.0",
"license": "OEEL-1",
"depends": [
"l10n_XX", "account_reports"
],
"data": [
"data/balance_sheet.xml",
"data/profit_and_loss.xml",
],
"auto_install": True,
}
Functional overview of financial reports is here: Main reports available.
Some good examples:
You can check the meaning of the fields here:
If you gave a root_report_id
to your report, it is now available in its variant selector. If not, you still need to add a menu item for it. A default menu item can be created from the form view of the report, by clicking on ‘Actions’, then ‘Create Menu Item’. You’ll then need to refresh the page to see it. Alternatively, to create a dedicated section for a totally new report in the Reporting menu, you need to create a new ir.ui.menu
record (usually in the main l10n_XX
module) and a new ir.actions.client
(usually in the new report xml file) that calls the account.report
with the new report id. Then, set the new menu as parent_id
field in the action model. Example for the Belgian localization: