From 60a8f894f942937125986df4e17688d7b6f5f358 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Fri, 6 Feb 2015 08:39:27 +0100 Subject: [PATCH] [ADD] l10n_eu_service As of January 1rst, 2015, telecommunications, broadcasting and electronic services sold within the European Union have to be always taxed in the country where the customer belongs. In order to simplify the application of this EU directive, the Mini One Stop Shop (MOSS) registration scheme allows businesses to make a unique tax declaration. This module makes it possible by helping with the creation of the required EU fiscal positions and taxes in order to automatically apply and record the required taxes. This module installs a wizard to help setup fiscal positions and taxes for selling electronic services inside EU. The wizard lets you select: - the EU countries to which you are selling these services - your national VAT tax for services, to be mapped to the target country's tax - optionally: a template fiscal position, in order to copy the account mapping. Should be your existing B2C Intra-EU fiscal position. (defaults to no account mapping) - optionally: an account to use for collecting the tax amounts (defaults to the account used by your national VAT tax for services) It creates the corresponding fiscal positions and taxes, automatically applicable for EU sales with a customer in the selected countries. The wizard can be run again for adding more countries. References ++++++++++ - Directive 2008/8/EC - Council Implementing Regulation (EU) No 1042/2013 Closes #5229 --- addons/l10n_eu_service/__init__.py | 2 + addons/l10n_eu_service/__openerp__.py | 85 ++++++++ .../l10n_eu_service/i18n/l10n_eu_service.pot | 184 ++++++++++++++++++ addons/l10n_eu_service/models/__init__.py | 1 + .../models/eu_service_tax_rate.py | 7 + .../security/ir.model.access.csv | 3 + addons/l10n_eu_service/wizard/__init__.py | 1 + .../l10n_eu_service.service_tax_rate.csv | 30 +++ addons/l10n_eu_service/wizard/wizard.py | 176 +++++++++++++++++ addons/l10n_eu_service/wizard/wizard.xml | 78 ++++++++ 10 files changed, 567 insertions(+) create mode 100644 addons/l10n_eu_service/__init__.py create mode 100644 addons/l10n_eu_service/__openerp__.py create mode 100644 addons/l10n_eu_service/i18n/l10n_eu_service.pot create mode 100644 addons/l10n_eu_service/models/__init__.py create mode 100644 addons/l10n_eu_service/models/eu_service_tax_rate.py create mode 100644 addons/l10n_eu_service/security/ir.model.access.csv create mode 100644 addons/l10n_eu_service/wizard/__init__.py create mode 100644 addons/l10n_eu_service/wizard/l10n_eu_service.service_tax_rate.csv create mode 100644 addons/l10n_eu_service/wizard/wizard.py create mode 100644 addons/l10n_eu_service/wizard/wizard.xml diff --git a/addons/l10n_eu_service/__init__.py b/addons/l10n_eu_service/__init__.py new file mode 100644 index 00000000000..7a3a680ead2 --- /dev/null +++ b/addons/l10n_eu_service/__init__.py @@ -0,0 +1,2 @@ +import wizard +import models \ No newline at end of file diff --git a/addons/l10n_eu_service/__openerp__.py b/addons/l10n_eu_service/__openerp__.py new file mode 100644 index 00000000000..1e840aee255 --- /dev/null +++ b/addons/l10n_eu_service/__openerp__.py @@ -0,0 +1,85 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (C) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +{ + 'name': 'EU Mini One Stop Shop (MOSS)', + 'version': '1.0', + 'author': 'Odoo SA', + 'website': 'http://www.odoo.com', + 'category': '', + 'description': """ +EU Mini One Stop Shop (MOSS) VAT for telecommunications, broadcasting and electronic services +============================================================================================= + +As of January 1rst, 2015, telecommunications, broadcasting +and electronic services sold within the European Union +have to be always taxed in the country where the customer +belongs. In order to simplify the application of this EU +directive, the Mini One Stop Shop (MOSS) registration scheme +allows businesses to make a unique tax declaration. + +This module makes it possible by helping with the creation +of the required EU fiscal positions and taxes in order to +automatically apply and record the required taxes. + +This module installs a wizard to help setup fiscal positions +and taxes for selling electronic services inside EU. + +The wizard lets you select: + - the EU countries to which you are selling these + services + - your national VAT tax for services, to be mapped + to the target country's tax + - optionally: a template fiscal position, in order + to copy the account mapping. Should be your + existing B2C Intra-EU fiscal position. (defaults + to no account mapping) + - optionally: an account to use for collecting the + tax amounts (defaults to the account used by your + national VAT tax for services) + +It creates the corresponding fiscal positions and taxes, +automatically applicable for EU sales with a customer +in the selected countries. +The wizard can be run again for adding more countries. + +The wizard creates a separate Chart of Taxes for collecting the +VAT amounts of the MOSS declaration, so extracting the MOSS +data should be easy. +Look for a Chart of Taxes named "EU MOSS VAT Chart" in the +Taxes Report menu (Generic Accounting Report). + +References +++++++++++ +- Directive 2008/8/EC +- Council Implementing Regulation (EU) No 1042/2013 + + """, + 'depends': ['account_accountant'], + 'data': [ + 'security/ir.model.access.csv', + 'wizard/wizard.xml', + 'wizard/l10n_eu_service.service_tax_rate.csv' + ], + 'test': [], + 'demo': [], + 'auto_install': False, + 'installable': True, +} diff --git a/addons/l10n_eu_service/i18n/l10n_eu_service.pot b/addons/l10n_eu_service/i18n/l10n_eu_service.pot new file mode 100644 index 00000000000..d4dc478ff81 --- /dev/null +++ b/addons/l10n_eu_service/i18n/l10n_eu_service.pot @@ -0,0 +1,184 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_eu_service +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-02-17 14:34+0000\n" +"PO-Revision-Date: 2015-02-17 14:34+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,done_country_ids:0 +msgid "Already Supported" +msgstr "" + +#. module: l10n_eu_service +#: code:addons/l10n_eu_service/wizard/wizard.py:128 +#, python-format +msgid "Base - VAT for EU Services to %(country_name)s" +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Based on the options selected below, this wizard will create one\n" +" fiscal position mapping for each EU country to which you are selling\n" +" services.\n" +" Each fiscal position will automatically map your national VAT tax for\n" +" services to the corresponding VAT tax in the country your customer\n" +" belongs to." +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Cancel" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,chart_id:0 +msgid "Chart of Accounts" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,country_id:0 +msgid "Country" +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Create Fiscal Positions and Taxes" +msgstr "" + +#. module: l10n_eu_service +#: model:ir.model,name:l10n_eu_service.model_l10n_eu_service_wizard +msgid "Create fiscal positions for EU Service VAT" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,create_uid:0 +#: field:l10n_eu_service.wizard,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,create_date:0 +#: field:l10n_eu_service.wizard,create_date:0 +msgid "Created on" +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Current EU B2C Fiscal Position, if any" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,todo_country_ids:0 +msgid "EU Customers From" +msgstr "" + +#. module: l10n_eu_service +#: code:addons/l10n_eu_service/wizard/wizard.py:110 +#, python-format +msgid "EU MOSS VAT Chart - %(company)s" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,fiscal_position_id:0 +msgid "Fiscal Position" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,id:0 +#: field:l10n_eu_service.wizard,id:0 +msgid "ID" +msgstr "" + +#. module: l10n_eu_service +#: code:addons/l10n_eu_service/wizard/wizard.py:161 +#, python-format +msgid "Intra-EU B2C in %(country_name)s" +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Keep empty to use current Service VAT account" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,write_uid:0 +#: field:l10n_eu_service.wizard,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,write_date:0 +#: field:l10n_eu_service.wizard,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: l10n_eu_service +#: help:l10n_eu_service.wizard,account_collected_id:0 +msgid "Optional account to use for collecting tax amounts when selling services in each EU country selected below. If not set, the current collecting account of your Service VAT will be used." +msgstr "" + +#. module: l10n_eu_service +#: help:l10n_eu_service.wizard,fiscal_position_id:0 +msgid "Optional fiscal position to use as template for general account mapping. Should usually be your current Intra-EU B2B fiscal position. If not set, no general account mapping will be configured for EU fiscal positions." +msgstr "" + +#. module: l10n_eu_service +#: help:l10n_eu_service.wizard,tax_id:0 +msgid "Select your current VAT tax for services. This is the tax that will be mapped to the corresponding VAT tax in each EU country selected below." +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,tax_id:0 +msgid "Service VAT" +msgstr "" + +#. module: l10n_eu_service +#: view:account.config.settings:l10n_eu_service.view_account_config_settings_inherit +#: model:ir.actions.act_window,name:l10n_eu_service.action_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "Setup EU MOSS Taxes" +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.wizard,account_collected_id:0 +msgid "Tax Collection Account" +msgstr "" + +#. module: l10n_eu_service +#: code:addons/l10n_eu_service/wizard/wizard.py:36 +#, python-format +msgid "The Europe country group cannot be found. Please update the base module." +msgstr "" + +#. module: l10n_eu_service +#: field:l10n_eu_service.service_tax_rate,rate:0 +msgid "VAT Rate" +msgstr "" + +#. module: l10n_eu_service +#: code:addons/l10n_eu_service/wizard/wizard.py:132 +#, python-format +msgid "VAT for EU Services to %(country_name)s" +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "You can use the wizard again later to add more countries." +msgstr "" + +#. module: l10n_eu_service +#: view:l10n_eu_service.wizard:l10n_eu_service.eu_service_view +msgid "or" +msgstr "" + diff --git a/addons/l10n_eu_service/models/__init__.py b/addons/l10n_eu_service/models/__init__.py new file mode 100644 index 00000000000..a1d32381940 --- /dev/null +++ b/addons/l10n_eu_service/models/__init__.py @@ -0,0 +1 @@ +import eu_service_tax_rate \ No newline at end of file diff --git a/addons/l10n_eu_service/models/eu_service_tax_rate.py b/addons/l10n_eu_service/models/eu_service_tax_rate.py new file mode 100644 index 00000000000..1d6b213fd7d --- /dev/null +++ b/addons/l10n_eu_service/models/eu_service_tax_rate.py @@ -0,0 +1,7 @@ +from openerp import fields, models, api + +class service_tax_rate(models.Model): + _name = "l10n_eu_service.service_tax_rate" + + country_id = fields.Many2one('res.country', string='Country') + rate = fields.Float(string="VAT Rate") \ No newline at end of file diff --git a/addons/l10n_eu_service/security/ir.model.access.csv b/addons/l10n_eu_service/security/ir.model.access.csv new file mode 100644 index 00000000000..28c0a9d0b65 --- /dev/null +++ b/addons/l10n_eu_service/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_l10n_eu_service_service_tax_rate,access_l10n_eu_service_service_tax_rate,model_l10n_eu_service_service_tax_rate,,1,0,0,0 +access_l10n_eu_service_service_tax_rate,access_l10n_eu_service_service_tax_rate,model_l10n_eu_service_service_tax_rate,account.group_account_manager,1,1,1,1 \ No newline at end of file diff --git a/addons/l10n_eu_service/wizard/__init__.py b/addons/l10n_eu_service/wizard/__init__.py new file mode 100644 index 00000000000..12cdf3c5bec --- /dev/null +++ b/addons/l10n_eu_service/wizard/__init__.py @@ -0,0 +1 @@ +import wizard \ No newline at end of file diff --git a/addons/l10n_eu_service/wizard/l10n_eu_service.service_tax_rate.csv b/addons/l10n_eu_service/wizard/l10n_eu_service.service_tax_rate.csv new file mode 100644 index 00000000000..2f402f5d542 --- /dev/null +++ b/addons/l10n_eu_service/wizard/l10n_eu_service.service_tax_rate.csv @@ -0,0 +1,30 @@ +id,country_id:id,rate +eu_rate_at,base.at,0.21 +eu_rate_be,base.be,0.21 +eu_rate_bg,base.bg,0.20 +eu_rate_hr,base.hr,0.25 +eu_rate_cy,base.cy,0.21 +eu_rate_cz,base.cz,0.21 +eu_rate_dk,base.dk,0.25 +eu_rate_ee,base.ee,0.20 +eu_rate_fi,base.fi,0.24 +eu_rate_fr,base.fr,0.20 +eu_rate_de,base.de,0.19 +eu_rate_gr,base.gr,0.23 +eu_rate_hu,base.hu,0.27 +eu_rate_ie,base.ie,0.23 +eu_rate_it,base.it,0.22 +eu_rate_lv,base.lv,0.21 +eu_rate_lt,base.lt,0.21 +eu_rate_lu,base.lu,0.17 +eu_rate_mt,base.mt,0.18 +eu_rate_nl,base.nl,0.21 +eu_rate_pl,base.pl,0.23 +eu_rate_pt,base.pt,0.23 +eu_rate_ro,base.ro,0.24 +eu_rate_pt,base.pt,0.23 +eu_rate_sk,base.sk,0.20 +eu_rate_si,base.si,0.22 +eu_rate_es,base.es,0.21 +eu_rate_se,base.se,0.24 +eu_rate_uk,base.uk,0.20 \ No newline at end of file diff --git a/addons/l10n_eu_service/wizard/wizard.py b/addons/l10n_eu_service/wizard/wizard.py new file mode 100644 index 00000000000..39ef1a90656 --- /dev/null +++ b/addons/l10n_eu_service/wizard/wizard.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (C) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.exceptions import Warning +from openerp import fields, models, api +from openerp.tools.translate import _ + +class l10n_eu_service(models.TransientModel): + + """Create fiscal positions for EU Service VAT""" + + _name = "l10n_eu_service.wizard" + _description = __doc__ + + def _get_eu_res_country_group(self): + eu_group = self.env.ref("base.europe", raise_if_not_found=False) + if not eu_group: + raise Warning(_('The Europe country group cannot be found. ' + 'Please update the base module.')) + return eu_group + + def _default_chart_id(self): + user = self.env.user + return self.env['account.account'].search( + [('company_id', '=', user.company_id.id), ('parent_id', '=', False)], limit=1) + + def _default_fiscal_position_id(self): + user = self.env.user + eu_id = self._get_eu_res_country_group() + return self.env['account.fiscal.position'].search( + [('company_id', '=', user.company_id.id), ('vat_required', '=', True), + ('country_group_id.id', '=', eu_id.id)], limit=1) + + def _default_tax_id(self): + user = self.env.user + return self.env['account.tax'].search( + [('company_id', '=', user.company_id.id), ('type_tax_use', '=', 'sale'), + ('type', '=', 'percent'), ('account_collected_id', '!=', False), + ('tax_code_id', '!=', False)], limit=1, order='amount desc') + + def _default_done_country_ids(self): + user = self.env.user + eu_country_group = self._get_eu_res_country_group() + return eu_country_group.country_ids - self._default_todo_country_ids() - user.company_id.country_id + + def _default_todo_country_ids(self): + user = self.env.user + eu_country_group = self._get_eu_res_country_group() + eu_fiscal = self.env['account.fiscal.position'].search( + [('country_id', 'in', eu_country_group.country_ids.ids), + ('vat_required', '=', False), ('auto_apply', '=', True), + ('company_id', '=', user.company_id.id)]) + return eu_country_group.country_ids - eu_fiscal.mapped('country_id') - user.company_id.country_id + + chart_id = fields.Many2one( + "account.account", string="Chart of Accounts", required=True, default=_default_chart_id) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + related='chart_id.company_id', readonly=True) + fiscal_position_id = fields.Many2one( + 'account.fiscal.position', string='Fiscal Position', default=_default_fiscal_position_id, + help="Optional fiscal position to use as template for general account mapping. " + "Should usually be your current Intra-EU B2B fiscal position. " + "If not set, no general account mapping will be configured for EU fiscal positions.") + tax_id = fields.Many2one( + 'account.tax', string='Service VAT', required=True, default=_default_tax_id, + help="Select your current VAT tax for services. This is the tax that will be mapped " + "to the corresponding VAT tax in each EU country selected below.") + account_collected_id = fields.Many2one( + "account.account", string="Tax Collection Account", + help="Optional account to use for collecting tax amounts when selling services in " + "each EU country selected below. If not set, the current collecting account of " + "your Service VAT will be used.") + done_country_ids = fields.Many2many( + 'res.country', 'l10n_eu_service_country_rel_done', default=_default_done_country_ids, + string='Already Supported') + todo_country_ids = fields.Many2many( + 'res.country', 'l10n_eu_service_country_rel_todo', default=_default_todo_country_ids, + string='EU Customers From', required=True) + + @api.multi + def generate_eu_service(self): + imd = self.env['ir.model.data'] + tax_code = self.env['account.tax.code'] + tax_rate = self.env["l10n_eu_service.service_tax_rate"] + account_tax = self.env['account.tax'] + fpos = self.env['account.fiscal.position'] + chart_xid = 'l10n_eu_service.tax_chart_service_eu_company_%s' % self.company_id.name + chart = self.env.ref(chart_xid, raise_if_not_found=False) + if not chart: + vals = { + 'name': _("EU MOSS VAT Chart - %(company)s") % {'company': self.company_id.name}, + 'company_id': self.company_id.id, + 'parent_id': False + } + chart_id = tax_code.create(vals).id + vals_data = { + 'name': 'tax_chart_service_eu_company_%s'%(self.company_id.name), + 'model': 'account.tax.code', + 'module': 'l10n_eu_service', + 'res_id': chart_id, + 'noupdate': True, # Don't drop it when module is updated + } + imd.create(vals_data) + else: + chart_id = chart.id + for country in self.todo_country_ids: + format_params = {'country_name': country.name} + tx_base_code_data = { + 'name': _("Base - VAT for EU Services to %(country_name)s") % format_params, + 'code': "BASE-EU-VAT-%s" % country.code, + 'parent_id': chart_id, + } + tax_name = _("VAT for EU Services to %(country_name)s") % format_params + tx_code_data = { + 'name': tax_name, + 'code': "EU-VAT-%s" % country.code, + 'parent_id': chart_id, + } + tx_base_code = tax_code.create(tx_base_code_data) + tx_code = tax_code.create(tx_code_data) + #create a new tax based on the selected service tax + data_tax = { + 'name': tax_name, + 'amount': tax_rate.search([('country_id', '=', country.id)]).rate, + 'base_code_id': self.tax_id.base_code_id.id, + 'account_collected_id': self.account_collected_id.id or self.tax_id.account_collected_id.id, + 'account_paid_id': self.account_collected_id.id or self.tax_id.account_collected_id.id, + 'type_tax_use': 'sale', + 'base_code_id': tx_base_code.id, + 'ref_base_code_id': tx_base_code.id, + 'tax_code_id': tx_code.id, + 'ref_tax_code_id': tx_code.id, + 'ref_base_sign': -1, + 'ref_tax_sign': -1 + } + tax = account_tax.create(data_tax) + if self.fiscal_position_id: + account_ids = [(6, 0, self.fiscal_position_id.account_ids.ids)] + else: + account_ids = False + #create a fiscal position for the country + fiscal_pos_name = _("Intra-EU B2C in %(country_name)s") % {'country_name': country.name} + fiscal_pos_name += " (EU-VAT-%s)" % country.code + data_fiscal = { + 'name': fiscal_pos_name, + 'company_id': self.chart_id.company_id.id, + 'vat_required': False, + 'auto_apply': True, + 'country_id': country.id, + 'account_ids': account_ids, + 'tax_ids': [(0, 0, {'tax_src_id': self.tax_id.id, 'tax_dest_id': tax.id})], + } + fpos.create(data_fiscal) + + return {'type': 'ir.actions.act_window_close'} + + diff --git a/addons/l10n_eu_service/wizard/wizard.xml b/addons/l10n_eu_service/wizard/wizard.xml new file mode 100644 index 00000000000..e419c027b2d --- /dev/null +++ b/addons/l10n_eu_service/wizard/wizard.xml @@ -0,0 +1,78 @@ + + + + + + l10n_eu_service.wizard.form + l10n_eu_service.wizard + +
+

+ Based on the options selected below, this wizard will create one + fiscal position mapping for each EU country to which you are selling + services. + Each fiscal position will automatically map your national VAT tax for + services to the corresponding VAT tax in the country your customer + belongs to. +
+ You can use the wizard again later to add more countries. +

+ + + + + + + + + +
+
+
+
+
+ + + Setup EU MOSS Taxes + l10n_eu_service.wizard + form + + new + + + + + automatic + open + + + + account settings + account.config.settings + + + +