diff --git a/intrastat_product/README.rst b/intrastat_product/README.rst index 455c358..1ad1510 100644 --- a/intrastat_product/README.rst +++ b/intrastat_product/README.rst @@ -1,3 +1,8 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== Intrastat Product Module ======================== @@ -8,6 +13,8 @@ such as: - *l10n_fr_intrastat_product*: the module for the *Déclaration d'Echange de Biens* (DEB) for France +- *l10n_be_intrastat_product*: + the module for the Intrastat Product Declaration for Belgium These country-specific modules can be found in the OCA localization for those countries. @@ -15,10 +22,74 @@ Installation ============ WARNING: + This module conflicts with the module *report_intrastat* from the official addons. If you have already installed the module *report_intrastat*, you should uninstall it before installing this module. + +Usage +===== + +This module is used in combination with the country-specific +localization module(s). + +Coding guidelines for localization module: +------------------------------------------ + +We recommend to start by copying an existing module, e.g. l10n_be_intrastat_product +and adapt the code for the specific needs of your country. + +* Declaration Object + + Create a new class as follows: + + .. code-block:: python + + class L10nCcIntrastatProductDeclaration(models.Model): + _name = 'l10n.cc.intrastat.product.declaration' + _description = "Intrastat Product Declaration for YourCountry" + _inherit = ['intrastat.product.declaration', 'mail.thread'] + + whereby cc = your country code + +* Computation & Declaration Lines + + Create also new objects inheriting from the Computation and Declaration Line Objects + so that you can add methods or customise the methods from the base modules (make a PR when + the customization or new method is required for multiple countries). + + Adapt also the parent_id fields of the newly created objects + (cf. l10n_be_intrastat_product as example). + +* XML Files: Menu, Action, Views + + Cf. l10n_be_istrastat_product as example, replace "be" by your Country Code. + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/91/8.0 + + +Known issues / Roadmap +====================== + +Work is in progress to migrate the existing l10n_fr_intrastat_product module +to this new reporting framework, cf. Alexis de Lattre, Akretion . + + +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 `here `_. + + Credits ======= @@ -26,16 +97,20 @@ Contributors ------------ * Alexis de Lattre, Akretion +* Luc De Meyer, Noviat + Maintainer ---------- -.. image:: http://odoo-community.org/logo.png +.. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association - :target: http://odoo-community.org + :target: https://odoo-community.org This module is maintained by the OCA. -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. +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. To contribute to this module, please visit http://odoo-community.org. diff --git a/intrastat_product/__init__.py b/intrastat_product/__init__.py index 063edb0..21ff7c2 100644 --- a/intrastat_product/__init__.py +++ b/intrastat_product/__init__.py @@ -1,7 +1,3 @@ # -*- encoding: utf-8 -*- -from . import res_company -from . import intrastat -from . import intrastat_declaration -from . import stock -from . import account_invoice +from . import models diff --git a/intrastat_product/__openerp__.py b/intrastat_product/__openerp__.py index 7a593e8..b9576da 100644 --- a/intrastat_product/__openerp__.py +++ b/intrastat_product/__openerp__.py @@ -3,7 +3,7 @@ # # Intrastat Product module for Odoo # Copyright (C) 2011-2015 Akretion (http://www.akretion.com) -# Copyright (C) 2015 Noviat (http://www.noviat.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) # @author Alexis de Lattre # @author Luc de Meyer # @@ -24,7 +24,7 @@ { 'name': 'Intrastat Product', - 'version': '1.3', + 'version': '1.4', 'category': 'Intrastat', 'license': 'AGPL-3', 'summary': 'Base module for Intrastat Product', @@ -36,16 +36,21 @@ ], 'conflicts': ['report_intrastat'], 'data': [ - 'intrastat_view.xml', - 'intrastat_declaration_view.xml', - 'res_company_view.xml', - 'account_invoice_view.xml', - 'stock_view.xml', + 'views/hs_code.xml', + 'views/intrastat_region.xml', + 'views/intrastat_unit.xml', + 'views/intrastat_transaction.xml', + 'views/intrastat_transport_mode.xml', + 'views/intrastat_product_declaration.xml', + 'views/res_company.xml', + 'views/account_invoice.xml', + 'views/stock_picking.xml', + 'views/stock_warehouse.xml', 'security/intrastat_security.xml', 'security/ir.model.access.csv', 'data/intrastat_transport_mode.xml', 'data/intrastat_unit.xml', ], - 'demo': ['intrastat_demo.xml'], + 'demo': ['demo/intrastat_demo.xml'], 'installable': True, } diff --git a/intrastat_product/intrastat_demo.xml b/intrastat_product/demo/intrastat_demo.xml similarity index 100% rename from intrastat_product/intrastat_demo.xml rename to intrastat_product/demo/intrastat_demo.xml diff --git a/intrastat_product/intrastat.py b/intrastat_product/intrastat.py deleted file mode 100644 index b87953a..0000000 --- a/intrastat_product/intrastat.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# Intrastat Product module for Odoo -# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) -# Copyright (C) 2015 Noviat (http://www.noviat.com) -# @author Alexis de Lattre -# @author Luc de Meyer -# -# 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 import models, fields, api, _ -from openerp.exceptions import ValidationError - - -class IntrastatUnit(models.Model): - _name = 'intrastat.unit' - _description = 'Intrastat Supplementary Units' - - name = fields.Char( - string='Name', required=True) - description = fields.Char( - string='Description', required=True) - uom_id = fields.Many2one( - 'product.uom', string='Regular UoM', - help="Select the regular Unit of Measure of Odoo that corresponds " - "to this Intrastat Supplementary Unit.") - active = fields.Boolean( - string='Active', default=True) - - -class HSCode(models.Model): - _inherit = "hs.code" - - intrastat_unit_id = fields.Many2one( - 'intrastat.unit', string='Intrastat Supplementary Unit') - - @api.constrains('local_code') - def _hs_code(self): - if self.company_id.country_id.intrastat: - if not self.local_code.isdigit(): - raise ValidationError( - _("Intrastat Codes should only contain digits. " - "This is not the case for code '%s'.") - % self.local_code) - if len(self.local_code) != 8: - raise ValidationError( - _("Intrastat Codes should " - "contain 8 digits. This is not the case for " - "Intrastat Code '%s' which has %d digits.") - % (self.local_code, len(self.local_code))) - - -class IntrastatTransaction(models.Model): - _name = 'intrastat.transaction' - _description = "Intrastat Transaction" - _order = 'code' - _rec_name = 'display_name' - - @api.one - @api.depends('code', 'description') - def _compute_display_name(self): - display_name = self.code - if self.description: - display_name += ' ' + self.description - self.display_name = len(display_name) > 55 \ - and display_name[:55] + '...' \ - or display_name - - code = fields.Char(string='Code', required=True) - description = fields.Text(string='Description') - display_name = fields.Char( - compute='_compute_display_name', string="Display Name", readonly=True) - company_id = fields.Many2one( - 'res.company', string='Company', - default=lambda self: self.env['res.company']._company_default_get( - 'intrastat.transaction')) - - _sql_constraints = [( - 'intrastat_transaction_code_unique', - 'UNIQUE(code, company_id)', - 'Code must be unique.')] - - -class IntrastatTransportMode(models.Model): - _name = 'intrastat.transport_mode' - _description = "Intrastat Transport Mode" - _rec_name = 'display_name' - _order = 'code' - - @api.one - @api.depends('name', 'code') - def _display_name(self): - print "display_name self=", self - print "self.code=", self.code - print "self.name=", self.name - self.display_name = '%s. %s' % (self.code, self.name) - - display_name = fields.Char( - string='Display Name', compute='_display_name', store=True, - readonly=True) - code = fields.Char(string='Code', required=True) - name = fields.Char(string='Name', required=True, translate=True) - description = fields.Char(string='Description', translate=True) - - _sql_constraints = [( - 'intrastat_transport_code_unique', - 'UNIQUE(code)', - 'Code must be unique.')] diff --git a/intrastat_product/intrastat_view.xml b/intrastat_product/intrastat_view.xml deleted file mode 100644 index c61fc61..0000000 --- a/intrastat_product/intrastat_view.xml +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - intrastat.hs.code.tree - hs.code - - - - - - - - - - - intrastat.hs.code.form - hs.code - - - - - - - - - - - intrastat.unit.form - intrastat.unit - -
- - - - - - -
-
-
- - - intrastat.unit.tree - intrastat.unit - - - - - - - - - - - intrastat.unit.search - intrastat.unit - - - - - - - - - - - - - Supplementary Units - intrastat.unit - tree,form - - - - - - - intrastat.transaction_form - intrastat.transaction - -
- - - - - -
-
-
- - - intrastat.transaction_tree - intrastat.transaction - - - - - - - - - - - intrastat.transaction.mode.search - intrastat.transaction - - - - - - - - - - - - Transaction Types - intrastat.transaction - tree,form - - - - - - - - intrastat.transport.mode.form - intrastat.transport_mode - -
- - - - - -
-
-
- - - intrastat.transport.mode.tree - intrastat.transport_mode - - - - - - - - - - - intrastat.transport.mode.search - intrastat.transport_mode - - - - - - - - - Transport Modes - intrastat.transport_mode - tree,form - - - - -
-
diff --git a/intrastat_product/models/__init__.py b/intrastat_product/models/__init__.py new file mode 100644 index 0000000..66c0d7b --- /dev/null +++ b/intrastat_product/models/__init__.py @@ -0,0 +1,11 @@ +# -*- encoding: utf-8 -*- +from . import account_invoice +from . import hs_code +from . import intrastat_product_declaration +from . import intrastat_region +from . import intrastat_transaction +from . import intrastat_transport_mode +from . import intrastat_unit +from . import res_company +from . import stock_picking +from . import stock_warehouse diff --git a/intrastat_product/account_invoice.py b/intrastat_product/models/account_invoice.py similarity index 81% rename from intrastat_product/account_invoice.py rename to intrastat_product/models/account_invoice.py index 3179012..74f713a 100644 --- a/intrastat_product/account_invoice.py +++ b/intrastat_product/models/account_invoice.py @@ -1,10 +1,11 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Copyright (c) 2012-2015 Noviat nv/sa (www.noviat.com) -# Copyright (C) 2015 Akretion (http://www.akretion.com) -# @author Luc de Meyer +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) # @author Alexis de Lattre +# @author Luc de Meyer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -13,11 +14,11 @@ # # 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 +# 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 . +# along with this program. If not, see . # ############################################################################## @@ -27,14 +28,14 @@ from openerp import models, fields, api class AccountInvoice(models.Model): _inherit = 'account.invoice' - @api.model - def _default_intrastat_transaction(self): - """ placeholder for localisation modules """ - return self.env['intrastat.transaction'].browse([]) - + incoterm_id = fields.Many2one( + 'stock.incoterms', string='Incoterm', + help="International Commercial Terms are a series of predefined " + "commercial terms used in international transactions.") intrastat_transaction_id = fields.Many2one( 'intrastat.transaction', string='Intrastat Transaction Type', - default=_default_intrastat_transaction, ondelete='restrict', + default=lambda self: self._default_intrastat_transaction(), + ondelete='restrict', help="Intrastat nature of transaction") intrastat_transport_id = fields.Many2one( 'intrastat.transport_mode', string='Intrastat Transport Mode', @@ -49,6 +50,11 @@ class AccountInvoice(models.Model): string='Intrastat Declaration', related='company_id.intrastat', store=True, readonly=True) + @api.model + def _default_intrastat_transaction(self): + """ placeholder for localisation modules """ + return self.env['intrastat.transaction'].browse([]) + @api.multi def onchange_partner_id( self, type, partner_id, date_invoice=False, @@ -82,7 +88,9 @@ class AccountInvoiceLine(models.Model): if product: product = self.env['product.product'].browse(product) - hs_code = product.get_hs_code_recursively() + hs_code = product.product_tmpl_id.get_hs_code_recursively() if hs_code: res['value']['hs_code_id'] = hs_code.id + else: + res['value']['hs_code_id'] = False return res diff --git a/intrastat_product/models/hs_code.py b/intrastat_product/models/hs_code.py new file mode 100644 index 0000000..6e23770 --- /dev/null +++ b/intrastat_product/models/hs_code.py @@ -0,0 +1,48 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) +# @author Alexis de Lattre +# @author Luc de Meyer +# +# 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 import models, fields, api, _ +from openerp.exceptions import ValidationError + + +class HSCode(models.Model): + _inherit = "hs.code" + + intrastat_unit_id = fields.Many2one( + 'intrastat.unit', string='Intrastat Supplementary Unit') + + @api.constrains('local_code') + def _hs_code(self): + if self.company_id.country_id.intrastat: + if not self.local_code.isdigit(): + raise ValidationError( + _("Intrastat Codes should only contain digits. " + "This is not the case for code '%s'.") + % self.local_code) + if len(self.local_code) != 8: + raise ValidationError( + _("Intrastat Codes should " + "contain 8 digits. This is not the case for " + "Intrastat Code '%s' which has %d digits.") + % (self.local_code, len(self.local_code))) diff --git a/intrastat_product/intrastat_declaration.py b/intrastat_product/models/intrastat_product_declaration.py similarity index 87% rename from intrastat_product/intrastat_declaration.py rename to intrastat_product/models/intrastat_product_declaration.py index 9f4f731..4dfc52b 100644 --- a/intrastat_product/intrastat_declaration.py +++ b/intrastat_product/models/intrastat_product_declaration.py @@ -3,7 +3,9 @@ # # Intrastat Product module for Odoo # Copyright (C) 2011-2015 Akretion (http://www.akretion.com) -# Copyright (C) 2011-2015 Noviat (http://www.noviat.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) +# @author Alexis de Lattre +# @author Luc de Meyer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -21,12 +23,11 @@ ############################################################################## from openerp import models, fields, api, _ -from openerp.exceptions import Warning, RedirectWarning, ValidationError +from openerp.exceptions import RedirectWarning, ValidationError, Warning import openerp.addons.decimal_precision as dp from datetime import datetime, date from dateutil.relativedelta import relativedelta import logging - _logger = logging.getLogger(__name__) @@ -105,6 +106,10 @@ class IntrastatProductDeclaration(models.Model): 'res.company', string='Company', readonly=True, default=lambda self: self.env['res.company']._company_default_get( 'intrastat.product.declaration')) + company_country_code = fields.Char( + compute='_compute_company_country_code', + string='Company Country Code', readonly=True, store=True, + help="Used in views and methods of localization modules.") year = fields.Integer( string='Year', required=True, default=_get_year) @@ -186,12 +191,19 @@ class IntrastatProductDeclaration(models.Model): _sql_constraints = [ ('date_uniq', - 'unique(year_month, company_id, type)', + 'unique(year_month, company_id, type, revision)', "A declaration of the same type already exists for this month !" "\nYou should update the existing declaration " "or change the revision number of this one."), ] + @api.one + @api.depends('company_id') + def _compute_company_country_code(self): + if self.company_id: + self.company_country_code = \ + self.company_id.country_id.code.lower() + @api.one @api.depends('year', 'month') def _compute_year_month(self): @@ -219,7 +231,7 @@ class IntrastatProductDeclaration(models.Model): _('Go to company configuration screen')) def _get_partner_country(self, inv_line): - country = inv_line.invoice_id.intrastat_country_id \ + country = inv_line.invoice_id.src_dest_country_id \ or inv_line.invoice_id.partner_id.country_id if not country.intrastat: country = False @@ -228,53 +240,13 @@ class IntrastatProductDeclaration(models.Model): return country def _get_intrastat_transaction(self, inv_line): - if inv_line.invoice_id.intrastat_transaction_id: - tr = inv_line.invoice_id.intrastat_transaction_id.code - else: - tr = self.env.ref( - 'l10n_be_intrastat_advanced.intrastat_transaction_1') - return tr - - def _get_region(self, inv_line): - """ - Logic copied from standard addons, l10n_be_intrastat module: - If purchase, comes from purchase order, linked to a location, - which is linked to the warehouse. - - If sales, the sale order is linked to the warehouse. - If sales, from a delivery order, linked to a location, - which is linked to the warehouse. - If none found, get the company one. - """ - region = False - if inv_line.invoice_id.type in ('in_invoice', 'in_refund'): - po_lines = self.env['purchase.order.line'].search( - [('invoice_lines', 'in', inv_line.id)]) - if po_lines: - po = po_lines.order_id - region = self.env['stock.warehouse'].get_region_from_location( - po.location_id) - elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'): - so_lines = self.env['sale.order.line'].search( - [('invoice_lines', 'in', inv_line.id)]) - if so_lines: - so = so_lines.order_id - region = so.warehouse_id.region_id - if not region: - if self.company_id.intrastat_region_id: - region = self.company_id.intrastat_region_id - else: - msg = _( - "The Intrastat Region of the Company is not set, " - "please configure it first.") - self._company_warning(msg) - return region + return inv_line.invoice_id.intrastat_transaction_id def _get_weight_and_supplunits(self, inv_line): line_qty = inv_line.quantity product = inv_line.product_id invoice = inv_line.invoice_id - intrastat_unit_id = inv_line.intrastat_id.intrastat_unit_id + intrastat_unit_id = inv_line.hs_code_id.intrastat_unit_id source_uom = inv_line.uos_id weight_uom_categ = self._uom_refs['weight_uom_categ'] kg_uom = self._uom_refs['kg_uom'] @@ -303,10 +275,10 @@ class IntrastatProductDeclaration(models.Model): "Please correct the Intrastat Supplementary Unit " "settingsand regenerate the lines or adjust the lines " "with Intrastat Code '%s' manually" - ) % intrastat_code + ) % inv_line.hs_code_id.local_code self._note += note return weight, suppl_unit_qty - if target_uom.categ_id == source_uom.category_id: + if target_uom.category_id == source_uom.category_id: suppl_unit_qty = self.env['product.uom']._compute_qty_obj( source_uom, line_qty, target_uom) else: @@ -370,8 +342,41 @@ class IntrastatProductDeclaration(models.Model): amount) return amount + def _get_region(self, inv_line): + """ + Logic copied from standard addons, l10n_be_intrastat module: + + If purchase, comes from purchase order, linked to a location, + which is linked to the warehouse. + + If sales, the sale order is linked to the warehouse. + If sales, from a delivery order, linked to a location, + which is linked to the warehouse. + + If none found, get the company one. + """ + region = False + if inv_line.invoice_id.type in ('in_invoice', 'in_refund'): + po_lines = self.env['purchase.order.line'].search( + [('invoice_lines', 'in', inv_line.id)]) + if po_lines: + po = po_lines.order_id + region = self.env['stock.warehouse'].get_region_from_location( + po.location_id) + elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'): + so_lines = self.env['sale.order.line'].search( + [('invoice_lines', 'in', inv_line.id)]) + if so_lines: + so = so_lines.order_id + region = so.warehouse_id.region_id + if not region: + if self.company_id.intrastat_region_id: + region = self.company_id.intrastat_region_id + return region + + def _get_transport(self, inv_line): - transport = inv_line.invoice.transport_mode_id \ + transport = inv_line.invoice_id.intrastat_transport_id \ or self.company_id.intrastat_transport_id if not transport: msg = _( @@ -382,8 +387,8 @@ class IntrastatProductDeclaration(models.Model): return transport def _get_incoterm(self, inv_line): - incoterm = inv_line.invoice.incoterm_id \ - or self.company_id.incoterm_id + incoterm = inv_line.invoice_id.incoterm_id \ + or self.company_id.intrastat_incoterm_id if not incoterm: msg = _( "The default Incoterm " @@ -392,9 +397,13 @@ class IntrastatProductDeclaration(models.Model): self._company_warning(msg) return incoterm + def _update_computation_line_vals(self, inv_line, line_vals): + """ placeholder for localization modules """ + pass + def _gather_invoices(self): - decl_lines = [] + lines = [] start_date = date(self.year, self.month, 1) end_date = start_date + relativedelta(day=1, months=+1, days=-1) @@ -416,7 +425,7 @@ class IntrastatProductDeclaration(models.Model): for inv_line in invoice.invoice_line: - intrastat = inv_line.intrastat_id + intrastat = inv_line.hs_code_id if not intrastat: continue if not inv_line.quantity: @@ -429,8 +438,6 @@ class IntrastatProductDeclaration(models.Model): intrastat_transaction = \ self._get_intrastat_transaction(inv_line) - region = self._get_region(inv_line) - weight, suppl_unit_qty = self._get_weight_and_supplunits( inv_line) @@ -439,33 +446,32 @@ class IntrastatProductDeclaration(models.Model): line_vals = { 'parent_id': self.id, 'invoice_line_id': inv_line.id, - 'partner_country_id': partner_country.id, + 'src_dest_country_id': partner_country.id, 'product_id': inv_line.product_id.id, - 'intrastat_code_id': intrastat.id, + 'hs_code_id': intrastat.id, 'weight': weight, 'suppl_unit_qty': suppl_unit_qty, 'amount_company_currency': amount_company_currency, 'transaction_id': intrastat_transaction.id, - 'region_id': region.id, - 'extended': self._extended, } # extended declaration if self._extended: transport = self._get_transport(inv_line) - incoterm = self._get_incoterm(inv_line) line_vals.update({ 'transport_id': transport.id, - 'incoterm_id': incoterm.id, }) - decl_lines.append((0, 0, line_vals)) + self._update_computation_line_vals(inv_line, line_vals) - return decl_lines + lines.append((line_vals)) + + return lines @api.multi def action_gather(self): self.ensure_one() + self._check_generate_lines() self._note = '' self._uom_refs = { 'weight_uom_categ': self.env.ref('product.product_uom_categ_kgm'), @@ -482,27 +488,23 @@ class IntrastatProductDeclaration(models.Model): else: self._extended = False - decl_lines_init = [(6, 0, [])] - decl_lines = decl_lines_init[:] + self.computation_line_ids.unlink() + lines = self._gather_invoices() - decl_lines += self._gather_invoices() - - if decl_lines == decl_lines_init: + if not lines: self.action = 'nihil' note = "\n" + \ _("No records found for the selected period !") + '\n' + \ _("The Declaration Action has been set to 'nihil'.") self._note += note - - # To DO: add check on tax cases 46, 48, 84, 86 - - self.write({'intrastat_line_ids': decl_lines}) + else: + self.write({'computation_line_ids': [(0, 0, x) for x in lines]}) if self._note: note_header = '\n\n>>> ' + str(date.today()) + '\n' - self.note = (self.note or '') + note_header + self._note + self.note = note_header + self._note + (self.note or '') result_view = self.env.ref( - 'l10n_be_intrastat_advanced.intrastat_result_view') + 'intrastat_base.intrastat_result_view_form') return { 'name': _("Generate lines from invoices: results"), 'view_type': 'form', @@ -518,12 +520,13 @@ class IntrastatProductDeclaration(models.Model): @api.model def group_line_hashcode(self, computation_line): - hashcode = "%s-%s-%s-%s-%s" % ( + hashcode = "%s-%s-%s-%s-%s-%s" % ( computation_line.src_dest_country_id.id or False, - computation_line.hs_code or False, + computation_line.hs_code_id.id or False, computation_line.intrastat_unit_id.id or False, computation_line.transaction_id.id or False, - computation_line.transport_id.id or False + computation_line.transport_id.id or False, + computation_line.region_id.id or False ) return hashcode @@ -542,13 +545,38 @@ class IntrastatProductDeclaration(models.Model): dl_group[hashcode].append(cl) else: dl_group[hashcode] = [cl] - ipdlo = self.pool['intrastat.product.declaration.line'] + ipdl = self.declaration_line_ids for cl_lines in dl_group.values(): - vals = ipdlo._prepare_declaration_line(cl_lines) - declaration_line = ipdlo.create(vals) - cl_lines.write({'declaration_line_id': declaration_line.id}) + vals = ipdl._prepare_declaration_line(cl_lines) + declaration_line = ipdl.create(vals) + for cl in cl_lines: + cl.write({'declaration_line_id': declaration_line.id}) return True + @api.multi + def generate_xml(self): + """ generate the INTRASTAT Declaration XML file """ + self.ensure_one() + self._check_generate_xml() + self._unlink_attachments() + xml_string = self._generate_xml() + if xml_string: + attach_id = self._attach_xml_file( + xml_string, '%s_%s' % (self.type, self.revision)) + return self._open_attach_view(attach_id) + else: + raise Warning( + _("Programming Error."), + _("No XML File has been generated.")) + + @api.multi + def done(self): + self.write({'state': 'done'}) + + @api.multi + def back2draft(self): + self.write({'state': 'draft'}) + class IntrastatProductComputationLine(models.Model): _name = 'intrastat.product.computation.line' @@ -612,7 +640,11 @@ class IntrastatProductComputationLine(models.Model): transaction_id = fields.Many2one( 'intrastat.transaction', string='Intrastat Transaction') + region_id = fields.Many2one( + 'intrastat.region', string='Intrastat Region') # extended declaration + incoterm_id = fields.Many2one( + 'stock.incoterms', string='Incoterm') transport_id = fields.Many2one( 'intrastat.transport_mode', string='Transport Mode') @@ -686,17 +718,21 @@ class IntrastatProductDeclarationLine(models.Model): transaction_id = fields.Many2one( 'intrastat.transaction', string='Intrastat Transaction') + region_id = fields.Many2one( + 'intrastat.region', string='Intrastat Region') # extended declaration + incoterm_id = fields.Many2one( + 'stock.incoterms', string='Incoterm') transport_id = fields.Many2one( 'intrastat.transport_mode', string='Transport Mode') @api.model - def _prepare_grouped_fields(computation_line, fields_to_sum): + def _prepare_grouped_fields(self, computation_line, fields_to_sum): vals = { 'src_dest_country_id': computation_line.src_dest_country_id.id, 'intrastat_unit_id': computation_line.intrastat_unit_id.id, - 'hs_code': computation_line.hs_code_id.local_code, + 'hs_code_id': computation_line.hs_code_id.id, 'transaction_id': computation_line.transaction_id.id, 'transport_id': computation_line.transport_id.id, 'parent_id': computation_line.parent_id.id, @@ -705,7 +741,7 @@ class IntrastatProductDeclarationLine(models.Model): vals[field] = 0.0 return vals - def _fields_to_sum(): + def _fields_to_sum(self): fields_to_sum = [ 'weight', 'suppl_unit_qty', diff --git a/intrastat_product/models/intrastat_region.py b/intrastat_product/models/intrastat_region.py new file mode 100644 index 0000000..3d1333e --- /dev/null +++ b/intrastat_product/models/intrastat_region.py @@ -0,0 +1,43 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Management Solution +# +# Copyright (c) 2009-2015 Noviat nv/sa (www.noviat.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 . +# +############################################################################## + +from openerp import models, fields + + +class IntrastatRegion(models.Model): + _name = 'intrastat.region' + _description = "Intrastat Region" + + code = fields.Char(string='Code', required=True) + country_id = fields.Many2one( + 'res.country', string='Country', required=True) + name = fields.Char(string='Name', translate=True) + description = fields.Char(string='Description') + company_id = fields.Many2one( + 'res.company', string='Company', + default=lambda self: self.env['res.company']._company_default_get( + 'intrastat.region')) + + _sql_constraints = [ + ('intrastat_region_code_unique', + 'UNIQUE(code, country_id)', + 'Code must be unique.')] diff --git a/intrastat_product/models/intrastat_transaction.py b/intrastat_product/models/intrastat_transaction.py new file mode 100644 index 0000000..3734fa6 --- /dev/null +++ b/intrastat_product/models/intrastat_transaction.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) +# @author Alexis de Lattre +# @author Luc de Meyer +# +# 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 import models, fields, api + + +class IntrastatTransaction(models.Model): + _name = 'intrastat.transaction' + _description = "Intrastat Transaction" + _order = 'code' + _rec_name = 'display_name' + + code = fields.Char(string='Code', required=True) + description = fields.Text(string='Description') + display_name = fields.Char( + compute='_compute_display_name', string="Display Name", readonly=True) + company_id = fields.Many2one( + 'res.company', string='Company', + default=lambda self: self.env['res.company']._company_default_get( + 'intrastat.transaction')) + + @api.one + @api.depends('code', 'description') + def _compute_display_name(self): + display_name = self.code + if self.description: + display_name += ' ' + self.description + self.display_name = len(display_name) > 55 \ + and display_name[:55] + '...' \ + or display_name + + _sql_constraints = [( + 'intrastat_transaction_code_unique', + 'UNIQUE(code, company_id)', + 'Code must be unique.')] diff --git a/intrastat_product/models/intrastat_transport_mode.py b/intrastat_product/models/intrastat_transport_mode.py new file mode 100644 index 0000000..6157459 --- /dev/null +++ b/intrastat_product/models/intrastat_transport_mode.py @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) +# @author Alexis de Lattre +# @author Luc de Meyer +# +# 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 import models, fields, api + + +class IntrastatTransportMode(models.Model): + _name = 'intrastat.transport_mode' + _description = "Intrastat Transport Mode" + _rec_name = 'display_name' + _order = 'code' + + display_name = fields.Char( + string='Display Name', compute='_display_name', store=True, + readonly=True) + code = fields.Char(string='Code', required=True) + name = fields.Char(string='Name', required=True, translate=True) + description = fields.Char(string='Description', translate=True) + + @api.one + @api.depends('name', 'code') + def _display_name(self): + self.display_name = '%s. %s' % (self.code, self.name) + + _sql_constraints = [( + 'intrastat_transport_code_unique', + 'UNIQUE(code)', + 'Code must be unique.')] diff --git a/intrastat_product/models/intrastat_unit.py b/intrastat_product/models/intrastat_unit.py new file mode 100644 index 0000000..65a86f2 --- /dev/null +++ b/intrastat_product/models/intrastat_unit.py @@ -0,0 +1,41 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) +# @author Alexis de Lattre +# @author Luc de Meyer +# +# 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 import models, fields + + +class IntrastatUnit(models.Model): + _name = 'intrastat.unit' + _description = 'Intrastat Supplementary Units' + + name = fields.Char( + string='Name', required=True) + description = fields.Char( + string='Description', required=True) + uom_id = fields.Many2one( + 'product.uom', string='Regular UoM', + help="Select the regular Unit of Measure of Odoo that corresponds " + "to this Intrastat Supplementary Unit.") + active = fields.Boolean( + string='Active', default=True) diff --git a/intrastat_product/res_company.py b/intrastat_product/models/res_company.py similarity index 76% rename from intrastat_product/res_company.py rename to intrastat_product/models/res_company.py index 65e0e3f..f3d4c9b 100644 --- a/intrastat_product/res_company.py +++ b/intrastat_product/models/res_company.py @@ -1,10 +1,11 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Copyright (c) 2012-2015 Noviat nv/sa (www.noviat.com) -# Copyright (C) 2015 Akretion (http://www.akretion.com) -# @author Luc de Meyer +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) # @author Alexis de Lattre +# @author Luc de Meyer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -13,11 +14,11 @@ # # 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 +# 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 . +# along with this program. If not, see . # ############################################################################## @@ -27,6 +28,28 @@ from openerp import models, fields, api class ResCompany(models.Model): _inherit = 'res.company' + intrastat_incoterm_id = fields.Many2one( + 'stock.incoterms', + string='Default incoterm for Intrastat', + help="International Commercial Terms are a series of " + "predefined commercial terms used in international " + "transactions.") + intrastat_arrivals = fields.Selection( + '_intrastat_arrivals', string='Arrivals', + default='extended', required=True) + intrastat_dispatches = fields.Selection( + '_intrastat_arrivals', string='Dispatches', + default='extended', required=True) + intrastat_transport_id = fields.Many2one( + 'intrastat.transport_mode', + string='Default Transport Mode', ondelete='restrict') + intrastat = fields.Char( + string='Intrastat Declaration', store=True, readonly=True, + compute='_compute_intrastat') + intrastat_region_id = fields.Many2one( + 'intrastat.region', + string='Default Intrastat region') + @api.model def _intrastat_arrivals(self): return [ @@ -52,16 +75,3 @@ class ResCompany(models.Model): self.intrastat = 'extended' else: self.intrastat = 'standard' - - intrastat_arrivals = fields.Selection( - '_intrastat_arrivals', string='Arrivals', - default='extended', required=True) - intrastat_dispatches = fields.Selection( - '_intrastat_arrivals', string='Dispatches', - default='extended', required=True) - intrastat_transport_id = fields.Many2one( - 'intrastat.transport_mode', - string='Default Transport Mode', ondelete='restrict') - intrastat = fields.Char( - string='Intrastat Declaration', store=True, readonly=True, - compute='_compute_intrastat') diff --git a/intrastat_product/stock.py b/intrastat_product/models/stock_picking.py similarity index 89% rename from intrastat_product/stock.py rename to intrastat_product/models/stock_picking.py index c97cf75..db97f5b 100644 --- a/intrastat_product/stock.py +++ b/intrastat_product/models/stock_picking.py @@ -1,8 +1,11 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Copyright (C) 2010-2015 Akretion (http://www.akretion.com) +# Intrastat Product module for Odoo +# Copyright (C) 2011-2015 Akretion (http://www.akretion.com) +# Copyright (C) 2009-2015 Noviat (http://www.noviat.com) # @author Alexis de Lattre +# @author Luc de Meyer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/intrastat_product/models/stock_warehouse.py b/intrastat_product/models/stock_warehouse.py new file mode 100644 index 0000000..d766b61 --- /dev/null +++ b/intrastat_product/models/stock_warehouse.py @@ -0,0 +1,42 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Management Solution +# +# Copyright (c) 2009-2015 Noviat nv/sa (www.noviat.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 . +# +############################################################################## + +from openerp import models, fields + + +class StockWarehouse(models.Model): + _inherit = 'stock.warehouse' + + region_id = fields.Many2one( + 'intrastat.region', + string='Intrastat region') + + def get_region_from_location(self, location): + locations = location.search( + [('parent_left', '<=', location.parent_left), + ('parent_right', '>=', location.parent_right)]) + warehouses = self.search( + [('lot_stock_id', 'in', [x.id for x in locations]), + ('region_id', '!=', False)]) + if warehouses: + return warehouses[0].region_id + return None diff --git a/intrastat_product/security/intrastat_security.xml b/intrastat_product/security/intrastat_security.xml index cd4f161..e483225 100644 --- a/intrastat_product/security/intrastat_security.xml +++ b/intrastat_product/security/intrastat_security.xml @@ -8,5 +8,11 @@ ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])] + + Intrastat Region Company rule + + ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])] + + diff --git a/intrastat_product/security/ir.model.access.csv b/intrastat_product/security/ir.model.access.csv index bd6835f..4b06ab2 100644 --- a/intrastat_product/security/ir.model.access.csv +++ b/intrastat_product/security/ir.model.access.csv @@ -5,6 +5,8 @@ access_intrastat_transaction_read,Read access on Intrastat Transaction Types to access_intrastat_transaction_full,Full access on Intrastat Transaction Types to Finance manager,model_intrastat_transaction,account.group_account_manager,1,1,1,1 access_intrastat_transport_mode_read,Read access on Intrastat Transport Modes to everybody,model_intrastat_transport_mode,,1,0,0,0 access_intrastat_transport_mode_full,Full access on Intrastat Transport Modes to Finance manager,model_intrastat_transport_mode,account.group_account_manager,1,1,1,1 +access_intrastat_region_read,Read access on Intrastat Regions,model_intrastat_region,,1,0,0,0 +access_intrastat_region_full,Full access on Intrastat Regions,model_intrastat_region,account.group_account_manager,1,1,1,1 access_intrastat_product_declaration,Full access on Intrastat Product Declarations to Accountant,model_intrastat_product_declaration,account.group_account_user,1,1,1,1 access_intrastat_product_computation_line,Full access on Intrastat Product Computation Lines to Accountant,model_intrastat_product_computation_line,account.group_account_user,1,1,1,1 access_intrastat_product_declaration_line,Full access on Intrastat Product Declaration Lines to Accountant,model_intrastat_product_declaration_line,account.group_account_user,1,1,1,1 diff --git a/intrastat_product/account_invoice_view.xml b/intrastat_product/views/account_invoice.xml similarity index 82% rename from intrastat_product/account_invoice_view.xml rename to intrastat_product/views/account_invoice.xml index cccb557..709ebbb 100644 --- a/intrastat_product/account_invoice_view.xml +++ b/intrastat_product/views/account_invoice.xml @@ -7,6 +7,10 @@ account.invoice + + + @@ -26,6 +30,10 @@ account.invoice + + + diff --git a/intrastat_product/views/hs_code.xml b/intrastat_product/views/hs_code.xml new file mode 100644 index 0000000..f6e54f2 --- /dev/null +++ b/intrastat_product/views/hs_code.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + intrastat.hs.code.tree + hs.code + + + + + + + + + + + intrastat.hs.code.form + hs.code + + + + + + + + + + diff --git a/intrastat_product/intrastat_declaration_view.xml b/intrastat_product/views/intrastat_product_declaration.xml similarity index 82% rename from intrastat_product/intrastat_declaration_view.xml rename to intrastat_product/views/intrastat_product_declaration.xml index 100eb93..935a158 100644 --- a/intrastat_product/intrastat_declaration_view.xml +++ b/intrastat_product/views/intrastat_product_declaration.xml @@ -2,7 +2,7 @@ - + intrastat.product.declaration.form intrastat.product.declaration @@ -12,7 +12,13 @@ attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('action', '=', 'nihil')]}" string="Generate lines from invoices" class="oe_highlight"/> -