From 769956e322574c99d591f2fa11844e0e1b1d916b Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 24 Apr 2013 10:55:14 +0200 Subject: [PATCH] [MIG] intrastat_base: Migration to 7.0 Should now be compatible with both OCB and RS-OCB Add field import_obligation_level on res.company Add group group_detailed_intrastat_product, so that companies that only use obligation = simplified don't see all the additionnal fields. Remove transaction code corresponding to repairs in intrastat types Better on_change on intrastat types (code is mutualised with field.function) Update syntax : demo_xml/update_xml/init_xml -> data/demo Welcome to Croatia in the European Union ! Add an e-mail reminder for l10n_fr_intrastat_product and l10n_fr_intrastat_service (hope that Akretion France won't forget it's own declarations now !!!) On report.intrastat.product and report.intrastat.service : add copy() fonctions, tracking of important fields, a year_month function field and enhance views. Remove date_done field (the tracking in the chatter does the job). Remove class instanciation in the code. Better form view of product category, courtesy of David Beal. Raise an explicit exception (take into account Stefan remark on the merge proposal) Fix to make the module truly usable when user is not part of the group "Detailed intrastat product". Remove dead code and fields that was used when we had to put DEB lines for repair operations (a thing of the past !). Update coding style. Reduce the number of flake8 warnings. On OpenERP 7, when you have the document module installed and you download the attachement via the drop down list on the form view, the name of the file will be the name of the attachement and not datas_fname ; so we need to have name = datas_fname. --- intrastat_base/__init__.py | 14 ++--- intrastat_base/__openerp__.py | 15 ++--- intrastat_base/company.py | 62 ++++++++++++++++++ intrastat_base/company_view.xml | 29 +++++++++ intrastat_base/country.py | 7 +-- intrastat_base/country_data.xml | 3 + intrastat_base/country_view.xml | 4 +- intrastat_base/intrastat_common.py | 63 ++++++++++--------- intrastat_base/intrastat_demo.xml | 8 +-- intrastat_base/intrastat_menu.xml | 2 +- .../{partner_address.py => partner.py} | 16 +++-- intrastat_base/product.py | 20 +++--- intrastat_base/product_view.xml | 32 ++++------ intrastat_base/tax.py | 9 +-- intrastat_base/tax_view.xml | 4 +- 15 files changed, 186 insertions(+), 102 deletions(-) create mode 100644 intrastat_base/company.py create mode 100644 intrastat_base/company_view.xml rename intrastat_base/{partner_address.py => partner.py} (77%) diff --git a/intrastat_base/__init__.py b/intrastat_base/__init__.py index 13ca149..8e2eaa0 100644 --- a/intrastat_base/__init__.py +++ b/intrastat_base/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved +# Copyright (C) 2011-2013 Akretion (http://www.akretion.com) # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,9 +20,9 @@ # ############################################################################## -import country -import product -import tax -import partner_address -import intrastat_common - +from . import country +from . import product +from . import tax +from . import partner +from . import company +from . import intrastat_common diff --git a/intrastat_base/__openerp__.py b/intrastat_base/__openerp__.py index 089173a..973786b 100644 --- a/intrastat_base/__openerp__.py +++ b/intrastat_base/__openerp__.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved +# Copyright (C) 2011-2013 Akretion (http://www.akretion.com) # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -22,14 +22,15 @@ { - 'name': 'Base module for Intrastat reporting', + 'name': 'Intrastat Reporting Base', 'version': '1.1', 'category': 'Localisation/Report Intrastat', 'license': 'AGPL-3', + 'summary': 'Base module for Intrastat reporting', 'description': """This module contains the common functions for 2 other modules : - l10n_fr_intrastat_service : the module for the "Déclaration Européenne des Services" (DES) - l10n_fr_intrastat_product : the module for the "Déclaration d'Echange de Biens" (DEB) -This module is not usefull if it's not used together with one of those 2 modules. +This module is not usefull if it's not used together with one of those 2 modules or other country-specific intrastat modules. This module doesn't have any France-specific stuff. So it can be used as a basis for other intrastat modules for other EU countries. @@ -40,15 +41,15 @@ Please contact Alexis de Lattre from Akretion for 'author': 'Akretion', 'website': 'http://www.akretion.com', 'depends': ['base_vat'], - 'init_xml': ['country_data.xml'], - 'update_xml': [ - 'security/ir.model.access.csv', + 'data': [ + 'country_data.xml', 'product_view.xml', 'country_view.xml', 'tax_view.xml', + 'company_view.xml', 'intrastat_menu.xml', ], - 'demo_xml': ['intrastat_demo.xml'], + 'demo': ['intrastat_demo.xml'], 'installable': True, 'active': False, } diff --git a/intrastat_base/company.py b/intrastat_base/company.py new file mode 100644 index 0000000..69163f5 --- /dev/null +++ b/intrastat_base/company.py @@ -0,0 +1,62 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Intrastat base module for OpenERP +# Copyright (C) 2013 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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.osv import orm, fields +from openerp.tools.translate import _ + + +class res_company(orm.Model): + _inherit = "res.company" + + def _compute_intrastat_email_list(self, cr, uid, ids, name, arg, context=None): + result = {} + for company in self.browse(cr, uid, ids, context=context): + result[company.id] = '' + for user in company.intrastat_remind_user_ids: + if result[company.id]: + result[company.id] += ',%s' % (user.email) + else: + result[company.id] = user.email + return result + + _columns = { + 'intrastat_remind_user_ids': fields.many2many('res.users', + id1='company_id', id2='user_id', + string="Users Receiving the Intrastat Reminder", + help="List of OpenERP users who will receive a notification to remind them about the Intrastat declaration."), + 'intrastat_email_list': fields.function(_compute_intrastat_email_list, + type='char', size=1000, + string='List of emails of Users Receiving the Intrastat Reminder', + help='Comma-separated list of email addresses of Users Receiving the Intrastat Reminder. For use in the email template.'), + } + + def _check_intrastat_remind_users(self, cr, uid, ids): + for company in self.browse(cr, uid, ids): + for user in company.intrastat_remind_user_ids: + if not user.email: + raise orm.except_orm(_('Error :'), _("Missing e-mail address on user '%s'.") % (user.name)) + return True + + _constraints = [ + (_check_intrastat_remind_users, "error msg in raise", + ['intrastat_remind_user_ids']), + ] diff --git a/intrastat_base/company_view.xml b/intrastat_base/company_view.xml new file mode 100644 index 0000000..14cf1fe --- /dev/null +++ b/intrastat_base/company_view.xml @@ -0,0 +1,29 @@ + + + + + + + + + intrastat.company.form + res.company + + + + + + + + + + + + + + + diff --git a/intrastat_base/country.py b/intrastat_base/country.py index 2542d72..2cb923f 100644 --- a/intrastat_base/country.py +++ b/intrastat_base/country.py @@ -20,9 +20,10 @@ # ############################################################################## -from osv import osv, fields +from openerp.osv import orm, fields -class res_country(osv.osv): + +class res_country(orm.Model): _inherit = 'res.country' _columns = { 'intrastat': fields.boolean('EU country', help="Set to True for all European Union countries."), @@ -31,5 +32,3 @@ class res_country(osv.osv): _defaults = { 'intrastat': False, } - -res_country() diff --git a/intrastat_base/country_data.xml b/intrastat_base/country_data.xml index 04fb439..c77f548 100644 --- a/intrastat_base/country_data.xml +++ b/intrastat_base/country_data.xml @@ -83,5 +83,8 @@ + + + diff --git a/intrastat_base/country_view.xml b/intrastat_base/country_view.xml index bf62fa3..87e7ec9 100644 --- a/intrastat_base/country_view.xml +++ b/intrastat_base/country_view.xml @@ -1,7 +1,7 @@ @@ -26,7 +26,7 @@ - + diff --git a/intrastat_base/intrastat_common.py b/intrastat_base/intrastat_common.py index 8320b8a..7a21a62 100644 --- a/intrastat_base/intrastat_common.py +++ b/intrastat_base/intrastat_common.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2010-2011 Akretion (http://www.akretion.com/). All rights reserved. +# Copyright (C) 2010-2013 Akretion (http://www.akretion.com/). All rights reserved. # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,12 +20,16 @@ # ############################################################################## -from osv import osv, fields +from openerp.osv import orm +from openerp.tools.translate import _ from datetime import datetime from dateutil.relativedelta import relativedelta -from tools.translate import _ +import logging -class report_intrastat_common(osv.osv_memory): +logger = logging.getLogger(__name__) + + +class report_intrastat_common(orm.TransientModel): _name = "report.intrastat.common" _description = "Common functions for intrastat reports for products and services" @@ -40,16 +44,17 @@ class report_intrastat_common(osv.osv_memory): result[intrastat.id] = {'num_lines': num_lines, 'total_amount': total_amount} return result - - def _compute_end_date(self, cr, uid, ids, object, context=None): + def _compute_dates(self, cr, uid, ids, object, context=None): result = {} for intrastat in object.browse(cr, uid, ids, context=context): start_date_datetime = datetime.strptime(intrastat.start_date, '%Y-%m-%d') end_date_str = datetime.strftime(start_date_datetime + relativedelta(day=31), '%Y-%m-%d') - result[intrastat.id] = end_date_str + result[intrastat.id] = { + 'end_date': end_date_str, + 'year_month': start_date_datetime.strftime('%Y-%m'), + } return result - def _check_start_date(self, cr, uid, ids, object, context=None): '''Check that the start date is the first day of the month''' for date_to_check in object.read(cr, uid, ids, ['start_date'], context=context): @@ -58,54 +63,47 @@ class report_intrastat_common(osv.osv_memory): return False return True - def _check_generate_lines(self, cr, uid, intrastat, context=None): if not intrastat.company_id.country_id: - raise osv.except_osv(_('Error :'), _("The country is not set on the company '%s'.") %intrastat.company_id.name) + raise orm.except_orm(_('Error :'), _("The country is not set on the company '%s'.") % intrastat.company_id.name) if not intrastat.currency_id.name == 'EUR': - raise osv.except_osv(_('Error :'), _("The company currency must be 'EUR', but is currently '%s'.") %intrastat.currency_id.name) + raise orm.except_orm(_('Error :'), _("The company currency must be 'EUR', but is currently '%s'.") % intrastat.currency_id.name) return True - def _check_generate_xml(self, cr, uid, intrastat, context=None): if not intrastat.company_id.partner_id.vat: - raise osv.except_osv(_('Error :'), _("The VAT number is not set for the partner '%s'.") %intrastat.company_id.partner_id.name) + raise orm.except_orm(_('Error :'), _("The VAT number is not set for the partner '%s'.") % intrastat.company_id.partner_id.name) return True - def _check_xml_schema(self, cr, uid, xml_root, xml_string, xsd, context=None): '''Validate the XML file against the XSD''' from lxml import etree official_des_xml_schema = etree.XMLSchema(etree.fromstring(xsd)) - try: official_des_xml_schema.assertValid(xml_root) + try: + official_des_xml_schema.assertValid(xml_root) except Exception, e: # if the validation of the XSD fails, we arrive here import logging _logger = logging.getLogger(__name__) _logger.warning("The XML file is invalid against the XML Schema Definition") _logger.warning(xml_string) _logger.warning(e) - raise osv.except_osv(_('Error :'), _('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s.') % str(e)) + raise orm.except_orm(_('Error :'), _('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s.') % str(e)) return True - def _attach_xml_file(self, cr, uid, ids, object, xml_string, start_date_datetime, declaration_name, context=None): '''Attach the XML file to the report_intrastat_product/service object''' import base64 - if len(ids) != 1: - raise osv.except_osv(_('Error :'), 'Hara kiri in attach_xml_file') + assert len(ids) == 1, "Only one ID accepted" filename = datetime.strftime(start_date_datetime, '%Y-%m') + '_' + declaration_name + '.xml' - attach_name = declaration_name.upper() + ' ' + datetime.strftime(start_date_datetime, '%Y-%m') attach_obj = self.pool.get('ir.attachment') if not context: context = {} - context.update({'default_res_id' : ids[0], 'default_res_model': object._name}) - attach_id = attach_obj.create(cr, uid, {'name': attach_name, 'datas': base64.encodestring(xml_string), 'datas_fname': filename}, context=context) + context.update({'default_res_id': ids[0], 'default_res_model': object._name}) + attach_id = attach_obj.create(cr, uid, {'name': filename, 'datas': base64.encodestring(xml_string), 'datas_fname': filename}, context=context) return attach_id - def _open_attach_view(self, cr, uid, attach_id, title='XML file', context=None): '''Returns an action which opens the form view of the corresponding attachement''' - # Only works in v6 -> not used in v5 action = { 'name': title, 'view_type': 'form', @@ -115,11 +113,10 @@ class report_intrastat_common(osv.osv_memory): 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', - 'res_id': [attach_id], + 'res_id': attach_id, } return action - def partner_on_change(self, cr, uid, ids, partner_id=False): result = {} result['value'] = {} @@ -128,5 +125,15 @@ class report_intrastat_common(osv.osv_memory): result['value'].update({'partner_vat': company['vat']}) return result -report_intrastat_common() - + def send_reminder_email(self, cr, uid, company, module_name, template_xmlid, intrastat_id, context=None): + template_data = self.pool['ir.model.data'].get_object_reference(cr, uid, module_name, template_xmlid) + if template_data and template_data[0] == 'email.template': + template_id = template_data[1] + else: + raise orm.except_orm(_('Error :'), _("Wrong model for XMLID '%s.%s': model is '%s' and it should be 'email.template'.") % (module_name, template_xmlid, template_data[0])) + if company.intrastat_remind_user_ids: + self.pool['email.template'].send_mail(cr, uid, template_id, intrastat_id, context=context) + logger.info('Intrastat Reminder email has been sent (XMLID: %s).' % template_xmlid) + else: + logger.warning('The list of users receiving the Intrastat Reminder is empty on company %s' % company.name) + return True diff --git a/intrastat_base/intrastat_demo.xml b/intrastat_base/intrastat_demo.xml index d83640e..07c68ab 100644 --- a/intrastat_base/intrastat_demo.xml +++ b/intrastat_base/intrastat_demo.xml @@ -1,7 +1,7 @@ @@ -13,7 +13,7 @@ FR58441019213 - + BE0828696437 True @@ -23,7 +23,7 @@ True - + BE0884025633 True @@ -48,7 +48,7 @@ Shipping costs SHIP service - + 30 True diff --git a/intrastat_base/intrastat_menu.xml b/intrastat_base/intrastat_menu.xml index b078c6a..83434cb 100644 --- a/intrastat_base/intrastat_menu.xml +++ b/intrastat_base/intrastat_menu.xml @@ -1,7 +1,7 @@ diff --git a/intrastat_base/partner_address.py b/intrastat_base/partner.py similarity index 77% rename from intrastat_base/partner_address.py rename to intrastat_base/partner.py index f36329b..a87c0ed 100644 --- a/intrastat_base/partner_address.py +++ b/intrastat_base/partner.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2010-2011 Akretion (http://www.akretion.com/) All Rights Reserved +# Copyright (C) 2010-2013 Akretion (http://www.akretion.com/) # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,16 +20,14 @@ # ############################################################################## -from osv import osv, fields +from openerp.osv import orm, fields -# We want to have the country field on res_partner_address always set + +# We want to have the country field on res_partner always set # because the selection of invoices for intrastat reports is based -# on the country of the invoice partner address ! -class res_partner_address(osv.osv): - _inherit = 'res.partner.address' +# on the country of the invoice partner ! +class res_partner(orm.Model): + _inherit = 'res.partner' _columns = { 'country_id': fields.many2one('res.country', 'Country', required=True), } - -res_partner_address() - diff --git a/intrastat_base/product.py b/intrastat_base/product.py index c535a72..60132f0 100644 --- a/intrastat_base/product.py +++ b/intrastat_base/product.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2010-2012 Akretion (http://www.akretion.com/) All Rights Reserved +# Copyright (C) 2010-2013 Akretion (http://www.akretion.com/) # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,30 +20,28 @@ # ############################################################################## -from osv import osv, fields -from tools.translate import _ +from openerp.osv import orm, fields +from openerp.tools.translate import _ -class product_template(osv.osv): + +class product_template(orm.Model): _inherit = "product.template" _columns = { 'exclude_from_intrastat': fields.boolean('Exclude from Intrastat reports', help="If set to True, the product or service will not be taken into account for Intrastat Product or Service reports. So you should leave this field to False unless you have a very good reason."), - 'is_accessory_cost' : fields.boolean('Is accessory cost', help='Activate this option for shipping costs, packaging costs and all services related to the sale of products. This option is used for Intrastat reports.'), + 'is_accessory_cost': fields.boolean('Is accessory cost', help='Activate this option for shipping costs, packaging costs and all services related to the sale of products. This option is used for Intrastat reports.'), } _defaults = { 'exclude_from_intrastat': False, } - def _check_accessory_cost(self, cr, uid, ids): for product in self.browse(cr, uid, ids): if product.is_accessory_cost and product.type != 'service': - raise osv.except_osv(_('Error :'), _("The option 'Is accessory cost?' should only be activated on 'Service' products. You have activated this option for the product '%s' which is of type '%s'" % (product.name, product.type))) + raise orm.except_orm(_('Error :'), _("The option 'Is accessory cost?' should only be activated on 'Service' products. You have activated this option for the product '%s' which is of type '%s'" % (product.name, product.type))) return True _constraints = [ - (_check_accessory_cost, "Error msg is in raise", ['is_accessory_cost', 'type']) + (_check_accessory_cost, "Error msg is in raise", + ['is_accessory_cost', 'type']) ] - -product_template() - diff --git a/intrastat_base/product_view.xml b/intrastat_base/product_view.xml index 28b026e..fd7c2d4 100644 --- a/intrastat_base/product_view.xml +++ b/intrastat_base/product_view.xml @@ -1,7 +1,7 @@ @@ -15,17 +15,12 @@ product.product - - - - - - - - + + + - - + + @@ -35,17 +30,12 @@ product.template - - - - - - - - + + + - - + + diff --git a/intrastat_base/tax.py b/intrastat_base/tax.py index 4d12d32..5e933d1 100644 --- a/intrastat_base/tax.py +++ b/intrastat_base/tax.py @@ -2,7 +2,7 @@ ############################################################################## # # Report intrastat base module for OpenERP -# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved +# Copyright (C) 2011-2013 Akretion (http://www.akretion.com). All Rights Reserved # @author Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,14 +20,11 @@ # ############################################################################## -from osv import osv, fields +from openerp.osv import orm, fields -class account_tax(osv.osv): +class account_tax(orm.Model): _inherit = "account.tax" _columns = { 'exclude_from_intrastat_if_present': fields.boolean('Exclude invoice line from intrastat if this tax is present', help="If this tax is present on an invoice line, this invoice line will be skipped when generating Intrastat Product or Service lines from invoices."), } - -account_tax() - diff --git a/intrastat_base/tax_view.xml b/intrastat_base/tax_view.xml index e60a327..c481490 100644 --- a/intrastat_base/tax_view.xml +++ b/intrastat_base/tax_view.xml @@ -17,8 +17,8 @@ - - + +