diff options
| author | Goffin Simon <simon.goffin@outlook.com> | 2015-02-06 08:39:27 +0100 |
|---|---|---|
| committer | Olivier Dony <odo@openerp.com> | 2015-02-17 16:08:15 +0100 |
| commit | 60a8f894f942937125986df4e17688d7b6f5f358 (patch) | |
| tree | 71266d7ff3e123cc6e98fa5c54fe8d690c83d5ad /addons/l10n_eu_service/wizard/wizard.py | |
| parent | af94e5af6b6234d986085b7555a2356a14bdb686 (diff) | |
[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
Diffstat (limited to 'addons/l10n_eu_service/wizard/wizard.py')
| -rw-r--r-- | addons/l10n_eu_service/wizard/wizard.py | 176 |
1 files changed, 176 insertions, 0 deletions
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. <http://www.odoo.com> +# +# 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 <http://www.gnu.org/licenses/>. +# +############################################################################## + +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'} + + |
