diff --git a/l10n_nl_tax_statement_icp/README.rst b/l10n_nl_tax_statement_icp/README.rst new file mode 100644 index 000000000..7c8ef845b --- /dev/null +++ b/l10n_nl_tax_statement_icp/README.rst @@ -0,0 +1,106 @@ +========================= +Netherlands ICP Statement +========================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--netherlands-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-netherlands/tree/12.0/l10n_nl_tax_statement_icp + :alt: OCA/l10n-netherlands +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-netherlands-12-0/l10n-netherlands-12-0-l10n_nl_tax_statement_icp + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/176/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the *Netherlands BTW Statement* module (BTW aangifte report), by adding the statement for the *Intra-Community transactions declaration* (ICP declaration). + +The ICP declaration report is based on line *3b - Leveringen naar landen binnen de EU (omzet)* of the BTW aangifte report. +The period is also the same as the one selected in the BTW aangifte report. + +This ICP declaration report includes: + +* the VAT identification numbers of your customers; +* the total amount of intra-Community supplies from the Netherlands for every customer during the selected period. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +#. Follow the configuration steps as described in *l10n_nl_tax_statement* and set the tag *3b omzet* needed for this report. + +Usage +===== + +To use this module, you need to: + +#. Create a BTW Statement. +#. Post the BTW Statement: a new tab *ICP Statement* will be displayed; the tab contains the lines of the ICP declaration report. +#. In tab *ICP Statement* press the Update button in order to recompute the ICP statement lines. + +Printing a PDF report: + +#. If you need to print the report in PDF, open a statement form and click: `Print -> NL ICP Statement` + +Known issues / Roadmap +====================== + +* Add checks to avoid errors in the report, e.g. no VAT code, tax-code not matching fiscal position, etc.. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Onestein + +Contributors +~~~~~~~~~~~~ + +* Andrea Stirpe + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/l10n-netherlands `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_nl_tax_statement_icp/__init__.py b/l10n_nl_tax_statement_icp/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/l10n_nl_tax_statement_icp/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_nl_tax_statement_icp/__manifest__.py b/l10n_nl_tax_statement_icp/__manifest__.py new file mode 100644 index 000000000..76cc3a070 --- /dev/null +++ b/l10n_nl_tax_statement_icp/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2018 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Netherlands ICP Statement', + 'version': '12.0.1.0.0', + 'category': 'Localization', + 'license': 'AGPL-3', + 'author': 'Onestein, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/l10n-netherlands', + 'depends': [ + 'l10n_nl_tax_statement', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/l10n_nl_vat_statement_view.xml', + 'views/report_tax_statement.xml', + 'report/report_tax_statement.xml', + ], + 'installable': True, +} diff --git a/l10n_nl_tax_statement_icp/i18n/l10n_nl_tax_statement_icp.pot b/l10n_nl_tax_statement_icp/i18n/l10n_nl_tax_statement_icp.pot new file mode 100644 index 000000000..ebb83e90c --- /dev/null +++ b/l10n_nl_tax_statement_icp/i18n/l10n_nl_tax_statement_icp.pot @@ -0,0 +1,208 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_nl_tax_statement_icp +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \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_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "(product + service)" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Amount Product" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_amount_products +msgid "Amount Products" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Amount Service" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_amount_services +msgid "Amount Services" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_country_code +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Country Code" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_create_uid +msgid "Created by" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_create_date +msgid "Created on" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_currency_id +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Currency" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_format_amount_products +msgid "Format Amount Products" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_format_amount_services +msgid "Format Amount Services" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "ICP" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_ids +msgid "ICP Lines" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.view_l10n_nl_vat_report_form +msgid "ICP Statement" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.view_l10n_nl_vat_report_form +msgid "ICP Statement lines" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_id +msgid "ID" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model,name:l10n_nl_tax_statement_icp.model_l10n_nl_vat_statement_icp_line +msgid "Intra-Community transactions (ICP) line" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line___last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_write_date +msgid "Last Updated on" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.actions.report,name:l10n_nl_tax_statement_icp.action_report_tax_statement_icp +msgid "NL ICP Statement" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model,name:l10n_nl_tax_statement_icp.model_l10n_nl_vat_statement +msgid "Netherlands Vat Statement" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_partner_id +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Partner" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.view_l10n_nl_vat_report_form +msgid "Press the Update button in order to recompute the lines!" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_statement_id +msgid "Statement" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_tag_3b_omzet +msgid "Tag 3B Omzet" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_tag_3b_omzet_d +msgid "Tag 3B Omzet D" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: code:addons/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement.py:115 +#, python-format +msgid "Tag 3b omzet not configured for this Company! Check the NL BTW Tags Configuration." +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_total +msgid "Total ICP amount" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "Total amount" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,help:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_total +msgid "Total amount in currency of the statement." +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.view_l10n_nl_vat_report_form +msgid "Update" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: model:ir.model.fields,field_description:l10n_nl_tax_statement_icp.field_l10n_nl_vat_statement_icp_line_vat +#: model:ir.ui.view,arch_db:l10n_nl_tax_statement_icp.report_tax_statement_icp +msgid "VAT" +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: code:addons/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement_icp_line.py:61 +#, python-format +msgid "Wrong country code (%s) for ICP report. Please check your configuration." +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: code:addons/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement_icp_line.py:56 +#, python-format +msgid "Wrong country code (NL) for ICP report." +msgstr "" + +#. module: l10n_nl_tax_statement_icp +#: code:addons/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement.py:192 +#, python-format +msgid "You cannot modify a final statement!" +msgstr "" + diff --git a/l10n_nl_tax_statement_icp/models/__init__.py b/l10n_nl_tax_statement_icp/models/__init__.py new file mode 100644 index 000000000..e82e2b789 --- /dev/null +++ b/l10n_nl_tax_statement_icp/models/__init__.py @@ -0,0 +1,2 @@ +from . import l10n_nl_vat_statement +from . import l10n_nl_vat_statement_icp_line diff --git a/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement.py b/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement.py new file mode 100644 index 000000000..4c8329232 --- /dev/null +++ b/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement.py @@ -0,0 +1,201 @@ +# Copyright 2018 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class VatStatement(models.Model): + _inherit = 'l10n.nl.vat.statement' + + tag_3b_omzet = fields.Many2one( + 'account.account.tag', + compute='_compute_tag_3b_omzet' + ) + tag_3b_omzet_d = fields.Many2one( + 'account.account.tag', + compute='_compute_tag_3b_omzet' + ) + icp_line_ids = fields.One2many( + 'l10n.nl.vat.statement.icp.line', + 'statement_id', + string='ICP Lines', + readonly=True + ) + icp_total = fields.Monetary( + string='Total ICP amount', + readonly=True, + help='Total amount in currency of the statement.' + ) + + @api.depends('company_id') + def _compute_tag_3b_omzet(self): + ''' Computes Tag 3b omzet''' + for statement in self: + config = self.env['l10n.nl.vat.statement.config'].search([ + ('company_id', '=', statement.company_id.id) + ], limit=1) + statement.tag_3b_omzet = config.tag_3b_omzet + statement.tag_3b_omzet_d = config.tag_3b_omzet_d + + @api.multi + def _compute_icp_lines(self): + ''' Computes ICP lines for the report''' + IcpLine = self.env['l10n.nl.vat.statement.icp.line'] + for statement in self: + statement.icp_line_ids.unlink() + statement.icp_total = 0.0 + amounts_map = statement._get_partner_amounts_map() + for partner_id in amounts_map: + icp_values = self._prepare_icp_line(amounts_map[partner_id]) + icp_values['partner_id'] = partner_id + icp_values['statement_id'] = statement.id + newline = IcpLine.create(icp_values) + statement.icp_line_ids += newline + icp_total = newline.amount_products + newline.amount_services + statement.icp_total += icp_total + + @api.model + def _prepare_icp_line(self, partner_amounts): + ''' Prepares an internal data structure representing the ICP line''' + return { + 'country_code': partner_amounts['country_code'], + 'vat': partner_amounts['vat'], + 'amount_products': partner_amounts['amount_products'], + 'amount_services': partner_amounts['amount_services'], + 'currency_id': partner_amounts['currency_id'], + } + + @api.multi + def _is_3b_omzet_line(self, line): + self.ensure_one() + + tag_3b_omzet = self.tag_3b_omzet + for tax in line.tax_ids: + if tax.tag_ids.filtered( + lambda r: r == tag_3b_omzet): + return True + return False + + @api.multi + def _is_3b_omzet_diensten_line(self, line): + self.ensure_one() + + tag_3b_omzet_d = self.tag_3b_omzet_d + for tax in line.tax_ids: + if tax.tag_ids.filtered( + lambda r: r == tag_3b_omzet_d): + return True + return False + + @api.multi + def _get_partner_amounts_map(self): + ''' Generate an internal data structure representing the ICP line''' + self.ensure_one() + + partner_amounts_map = {} + for line in self.move_line_ids: + is_3b_omzet = self._is_3b_omzet_line(line) + is_3b_omzet_diensten = self._is_3b_omzet_diensten_line(line) + if is_3b_omzet or is_3b_omzet_diensten: + vals = self._prepare_icp_line_from_move_line(line) + if vals['partner_id'] not in partner_amounts_map: + self._init_partner_amounts_map(partner_amounts_map, vals) + self._update_partner_amounts_map(partner_amounts_map, vals) + return partner_amounts_map + + @api.multi + def _check_config_tag_3b_omzet(self): + ''' Checks the tag 3b Omzet, as configured for the BTW statement''' + if self.env.context.get('skip_check_config_tag_3b_omzet'): + return + for statement in self: + if not statement.tag_3b_omzet or not statement.tag_3b_omzet_d: + raise UserError( + _('Tag 3b omzet not configured for this Company! ' + 'Check the NL BTW Tags Configuration.')) + + @classmethod + def _update_partner_amounts_map(cls, partner_amounts_map, vals): + ''' Update amounts of the internal ICP lines data structure''' + map_data = partner_amounts_map[vals['partner_id']] + map_data['amount_products'] += vals['amount_products'] + map_data['amount_services'] += vals['amount_services'] + + @classmethod + def _init_partner_amounts_map(cls, partner_amounts_map, vals): + ''' Initialize the internal ICP lines data structure''' + partner_amounts_map[vals['partner_id']] = { + 'country_code': vals['country_code'], + 'vat': vals['vat'], + 'currency_id': vals['currency_id'], + 'amount_products': 0.0, + 'amount_services': 0.0, + } + + @api.multi + def _prepare_icp_line_from_move_line(self, line): + ''' Gets move line details and prepares ICP report line data''' + self.ensure_one() + + balance = line.balance + if line.company_currency_id != self.currency_id: + balance = line.company_currency_id.with_context( + date=line.date + ).compute(balance, self.currency_id, round=True) + amount_products = balance + amount_services = 0.0 + if self._is_3b_omzet_diensten_line(line): + amount_products = 0.0 + amount_services = balance + + return { + 'partner_id': line.partner_id.id, + 'country_code': line.partner_id.country_id.code, + 'vat': line.partner_id.vat, + 'amount_products': amount_products, + 'amount_services': amount_services, + 'currency_id': self.currency_id.id, + } + + @api.multi + def reset(self): + ''' Removes ICP lines if reset to draft''' + for statement in self: + statement.icp_line_ids.unlink() + return super().reset() + + @api.multi + def post(self): + ''' Checks configuration when validating the statement''' + self.ensure_one() + self._check_config_tag_3b_omzet() + res = super().post() + self._compute_icp_lines() + return res + + @api.model + def _modifiable_values_when_posted(self): + ''' Returns the modifiable fields even when the statement is posted''' + res = super()._modifiable_values_when_posted() + res.append('icp_line_ids') + res.append('icp_total') + return res + + @api.multi + def icp_update(self): + ''' Update button''' + self.ensure_one() + + if self.state in ['final']: + raise UserError( + _('You cannot modify a final statement!')) + + # clean old lines + self.icp_line_ids.unlink() + + # check config + self._check_config_tag_3b_omzet() + + # create lines + self._compute_icp_lines() diff --git a/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement_icp_line.py b/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement_icp_line.py new file mode 100644 index 000000000..a8e37363b --- /dev/null +++ b/l10n_nl_tax_statement_icp/models/l10n_nl_vat_statement_icp_line.py @@ -0,0 +1,63 @@ +# Copyright 2018 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, api, fields, models +from odoo.tools.misc import formatLang +from odoo.exceptions import ValidationError + + +class VatStatementIcpLine(models.Model): + _name = 'l10n.nl.vat.statement.icp.line' + _description = 'Intra-Community transactions (ICP) line' + _order = 'partner_id, country_code' + + statement_id = fields.Many2one( + 'l10n.nl.vat.statement', + 'Statement', + ondelete='cascade' + ) + partner_id = fields.Many2one( + 'res.partner', + string='Partner', + readonly=True, + required=True, + ) + vat = fields.Char( + string='VAT', + readonly=True, + ) + country_code = fields.Char( + readonly=True, + ) + currency_id = fields.Many2one( + 'res.currency', + string='Currency', + readonly=True + ) + amount_products = fields.Monetary(readonly=True) + format_amount_products = fields.Char(compute='_compute_icp_amount_format') + amount_services = fields.Monetary(readonly=True) + format_amount_services = fields.Char(compute='_compute_icp_amount_format') + + @api.depends('amount_products', 'amount_services') + def _compute_icp_amount_format(self): + for line in self: + amount_products = formatLang( + self.env, line.amount_products, monetary=True) + amount_services = formatLang( + self.env, line.amount_services, monetary=True) + line.format_amount_products = amount_products + line.format_amount_services = amount_services + + @api.constrains('country_code') + def _check_country_code(self): + country_codes = self.mapped('country_code') + if self.env.ref('base.nl').code in country_codes: + raise ValidationError(_( + 'Wrong country code (NL) for ICP report.')) + europe_codes = self.env.ref('base.europe').country_ids.mapped('code') + for code in country_codes: + if code not in europe_codes: + raise ValidationError(_( + 'Wrong country code (%s) for ICP report. ' + 'Please check your configuration.') % code) diff --git a/l10n_nl_tax_statement_icp/readme/CONFIGURE.rst b/l10n_nl_tax_statement_icp/readme/CONFIGURE.rst new file mode 100644 index 000000000..10b465533 --- /dev/null +++ b/l10n_nl_tax_statement_icp/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +To configure this module, you need to: + +#. Follow the configuration steps as described in *l10n_nl_tax_statement* and set the tag *3b omzet* needed for this report. diff --git a/l10n_nl_tax_statement_icp/readme/CONTRIBUTORS.rst b/l10n_nl_tax_statement_icp/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..4518218c8 --- /dev/null +++ b/l10n_nl_tax_statement_icp/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Andrea Stirpe diff --git a/l10n_nl_tax_statement_icp/readme/DESCRIPTION.rst b/l10n_nl_tax_statement_icp/readme/DESCRIPTION.rst new file mode 100644 index 000000000..0da447f3a --- /dev/null +++ b/l10n_nl_tax_statement_icp/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module extends the *Netherlands BTW Statement* module (BTW aangifte report), by adding the statement for the *Intra-Community transactions declaration* (ICP declaration). + +The ICP declaration report is based on line *3b - Leveringen naar landen binnen de EU (omzet)* of the BTW aangifte report. +The period is also the same as the one selected in the BTW aangifte report. + +This ICP declaration report includes: + +* the VAT identification numbers of your customers; +* the total amount of intra-Community supplies from the Netherlands for every customer during the selected period. diff --git a/l10n_nl_tax_statement_icp/readme/ROADMAP.rst b/l10n_nl_tax_statement_icp/readme/ROADMAP.rst new file mode 100644 index 000000000..ce55fc8b9 --- /dev/null +++ b/l10n_nl_tax_statement_icp/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Add checks to avoid errors in the report, e.g. no VAT code, tax-code not matching fiscal position, etc.. diff --git a/l10n_nl_tax_statement_icp/readme/USAGE.rst b/l10n_nl_tax_statement_icp/readme/USAGE.rst new file mode 100644 index 000000000..d7f748598 --- /dev/null +++ b/l10n_nl_tax_statement_icp/readme/USAGE.rst @@ -0,0 +1,9 @@ +To use this module, you need to: + +#. Create a BTW Statement. +#. Post the BTW Statement: a new tab *ICP Statement* will be displayed; the tab contains the lines of the ICP declaration report. +#. In tab *ICP Statement* press the Update button in order to recompute the ICP statement lines. + +Printing a PDF report: + +#. If you need to print the report in PDF, open a statement form and click: `Print -> NL ICP Statement` diff --git a/l10n_nl_tax_statement_icp/report/report_tax_statement.xml b/l10n_nl_tax_statement_icp/report/report_tax_statement.xml new file mode 100644 index 000000000..17b248a4c --- /dev/null +++ b/l10n_nl_tax_statement_icp/report/report_tax_statement.xml @@ -0,0 +1,60 @@ + + + + + + + + diff --git a/l10n_nl_tax_statement_icp/security/ir.model.access.csv b/l10n_nl_tax_statement_icp/security/ir.model.access.csv new file mode 100644 index 000000000..5eaba8224 --- /dev/null +++ b/l10n_nl_tax_statement_icp/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_l10n_nl_vat_statement_icp_line,access_l10n_nl_vat_statement_icp_line,model_l10n_nl_vat_statement_icp_line,account.group_account_user,1,1,1,1 diff --git a/l10n_nl_tax_statement_icp/static/description/index.html b/l10n_nl_tax_statement_icp/static/description/index.html new file mode 100644 index 000000000..e1a86b4d2 --- /dev/null +++ b/l10n_nl_tax_statement_icp/static/description/index.html @@ -0,0 +1,463 @@ + + + + + + +Netherlands ICP Statement + + + +
+

Netherlands ICP Statement

+ + +

Beta License: AGPL-3 OCA/l10n-netherlands Translate me on Weblate Try me on Runbot

+

This module extends the Netherlands BTW Statement module (BTW aangifte report), by adding the statement for the Intra-Community transactions declaration (ICP declaration).

+

The ICP declaration report is based on line 3b - Leveringen naar landen binnen de EU (omzet) of the BTW aangifte report. +The period is also the same as the one selected in the BTW aangifte report.

+

This ICP declaration report includes:

+
    +
  • the VAT identification numbers of your customers;
  • +
  • the total amount of intra-Community supplies from the Netherlands for every customer during the selected period.
  • +
+

Table of contents

+ +
+

Installation

+

To install this module, you need to:

+
    +
  1. Install module l10n_nl_tax_statement version >= 11.0.2.0.0.
  2. +
+
+
+

Configuration

+

To configure this module, you need to:

+
    +
  1. Follow the configuration steps as described in l10n_nl_tax_statement and set the tag 3b omzet needed for this report.
  2. +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  1. Create a BTW Statement.
  2. +
  3. Post the BTW Statement: a new tab ICP Statement will be displayed; the tab contains the lines of the ICP declaration report.
  4. +
  5. In tab ICP Statement press the Update button in order to recompute the ICP statement lines.
  6. +
+

Printing a PDF report:

+
    +
  1. If you need to print the report in PDF, open a statement form and click: Print -> NL ICP Statement
  2. +
+
+
+

Known issues / Roadmap

+
    +
  • Add checks to avoid errors in the report, e.g. no VAT code, tax-code not matching fiscal position, etc..
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Onestein
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/l10n-netherlands project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/l10n_nl_tax_statement_icp/tests/__init__.py b/l10n_nl_tax_statement_icp/tests/__init__.py new file mode 100644 index 000000000..51fbf3713 --- /dev/null +++ b/l10n_nl_tax_statement_icp/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_l10n_nl_tax_statement_icp diff --git a/l10n_nl_tax_statement_icp/tests/test_l10n_nl_tax_statement_icp.py b/l10n_nl_tax_statement_icp/tests/test_l10n_nl_tax_statement_icp.py new file mode 100644 index 000000000..83a1716a1 --- /dev/null +++ b/l10n_nl_tax_statement_icp/tests/test_l10n_nl_tax_statement_icp.py @@ -0,0 +1,139 @@ +# Copyright 2018 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.exceptions import UserError, ValidationError + +from odoo.addons.l10n_nl_tax_statement.tests.test_l10n_nl_vat_statement\ + import TestVatStatement + + +class TestTaxStatementIcp(TestVatStatement): + + def _prepare_icp_invoice(self): + for invoice_line in self.invoice_1.invoice_line_ids: + for tax_line in invoice_line.invoice_line_tax_ids: + tax_line.tag_ids = self.tag_3 + self.invoice_1._onchange_invoice_line_ids() + self.invoice_1.action_invoice_open() + self.statement_with_icp.statement_update() + + def test_01_compute_tag_3b_omzet(self): + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + self.assertEqual(self.statement_with_icp.tag_3b_omzet, self.tag_3) + self.assertEqual(self.statement_with_icp.tag_3b_omzet_d, self.tag_4) + + def test_02_no_tag_3b_omzet(self): + self.config.write({ + 'tag_3b_omzet': False, + 'tag_3b_omzet_d': False, + }) + self.statement_not_valid = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + self.statement_not_valid.statement_update() + with self.assertRaises(UserError): + self.statement_not_valid.post() + + def test_03_post_final(self): + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + # all previous statements must be already posted + self.statement_with_icp.statement_update() + with self.assertRaises(UserError): + self.statement_with_icp.post() + + self.statement_1.statement_update() + self.statement_1.post() + self.assertEqual(self.statement_1.state, 'posted') + + # first post + self.statement_with_icp.post() + + self.assertEqual(self.statement_with_icp.state, 'posted') + self.assertTrue(self.statement_with_icp.date_posted) + + self.statement_with_icp.icp_update() + + # then finalize + self.statement_with_icp.finalize() + self.assertEqual(self.statement_with_icp.state, 'final') + self.assertTrue(self.statement_with_icp.date_posted) + + with self.assertRaises(UserError): + self.statement_with_icp.icp_update() + + def test_04_icp_invoice(self): + self.statement_1.post() + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + self.invoice_1.partner_id.country_id = self.env.ref('base.be') + self._prepare_icp_invoice() + + self.statement_with_icp.post() + self.assertTrue(self.statement_with_icp.icp_line_ids) + self.assertTrue(self.statement_with_icp.icp_total) + + for icp_line in self.statement_with_icp.icp_line_ids: + self.assertTrue(icp_line.amount_products) + self.assertFalse(icp_line.amount_services) + amount_products = icp_line.format_amount_products + self.assertEqual(float(amount_products), icp_line.amount_products) + amount_services = icp_line.format_amount_services + self.assertEqual(float(amount_services), icp_line.amount_services) + + def test_05_icp_invoice_service(self): + self.statement_1.post() + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + self.invoice_1.partner_id.country_id = self.env.ref('base.be') + for invoice_line in self.invoice_1.invoice_line_ids: + for tax_line in invoice_line.invoice_line_tax_ids: + tax_line.tag_ids = self.tag_4 + self.invoice_1._onchange_invoice_line_ids() + self.invoice_1.action_invoice_open() + self.statement_with_icp.statement_update() + + self.statement_with_icp.post() + self.assertTrue(self.statement_with_icp.icp_line_ids) + self.assertTrue(self.statement_with_icp.icp_total) + + for icp_line in self.statement_with_icp.icp_line_ids: + self.assertFalse(icp_line.amount_products) + self.assertTrue(icp_line.amount_services) + amount_products = icp_line.format_amount_products + self.assertEqual(float(amount_products), icp_line.amount_products) + amount_services = icp_line.format_amount_services + self.assertEqual(float(amount_services), icp_line.amount_services) + + def test_06_icp_invoice_nl(self): + self.statement_1.post() + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + self.invoice_1.partner_id.country_id = self.env.ref('base.nl') + self._prepare_icp_invoice() + + with self.assertRaises(ValidationError): + self.statement_with_icp.post() + + def test_07_icp_invoice_outside_europe(self): + self.statement_1.post() + self.statement_with_icp = self.env['l10n.nl.vat.statement'].create({ + 'name': 'Statement 1', + }) + + self.invoice_1.partner_id.country_id = self.env.ref('base.us') + self._prepare_icp_invoice() + + with self.assertRaises(ValidationError): + self.statement_with_icp.post() diff --git a/l10n_nl_tax_statement_icp/views/l10n_nl_vat_statement_view.xml b/l10n_nl_tax_statement_icp/views/l10n_nl_vat_statement_view.xml new file mode 100644 index 000000000..7514e3c67 --- /dev/null +++ b/l10n_nl_tax_statement_icp/views/l10n_nl_vat_statement_view.xml @@ -0,0 +1,42 @@ + + + + + + + l10n.nl.vat.statement + + + + + +
Press the Update button in order to recompute the lines!
+
+
+
+ + + + + + + + + + + + + + + + +
+
+
+
+ +
diff --git a/l10n_nl_tax_statement_icp/views/report_tax_statement.xml b/l10n_nl_tax_statement_icp/views/report_tax_statement.xml new file mode 100644 index 000000000..74bcb5fcf --- /dev/null +++ b/l10n_nl_tax_statement_icp/views/report_tax_statement.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + +