From 3781f37bcb50ee5d2b4e27fbf2ffd9d5c9a15c79 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 6 Jun 2015 00:20:41 +0200 Subject: [PATCH] Port SEPA modules to new API Fix an important regression in account_banking_sepa_direct_debit: "Date of Last Debit" was not set any more Proper write of date_done with account_banking_payment_export is installed without account_banking_payment_transfer Add post-install script for date_sent on payment.order --- .../views/account_banking_mandate_view.xml | 7 +- account_banking_pain_base/__init__.py | 2 +- account_banking_pain_base/__openerp__.py | 4 +- account_banking_pain_base/models/__init__.py | 2 +- .../models/banking_export_pain.py | 180 +++++------ .../models/payment_line.py | 42 +-- .../models/payment_mode.py | 24 +- .../models/res_company.py | 2 +- account_banking_payment_export/__openerp__.py | 1 + .../models/account_payment.py | 8 + .../workflow/account_payment.xml | 16 + account_banking_payment_transfer/__init__.py | 1 + .../__openerp__.py | 1 + .../model/account_payment.py | 4 +- .../post_install.py | 25 ++ .../workflow/account_payment.xml | 2 - .../wizard/__init__.py | 2 +- .../wizard/export_sepa.py | 189 +++++------ .../wizard/export_sdd.py | 295 ++++++++---------- 19 files changed, 379 insertions(+), 428 deletions(-) create mode 100644 account_banking_payment_export/workflow/account_payment.xml create mode 100644 account_banking_payment_transfer/post_install.py diff --git a/account_banking_mandate/views/account_banking_mandate_view.xml b/account_banking_mandate/views/account_banking_mandate_view.xml index 172eadbb3..d4c016e02 100644 --- a/account_banking_mandate/views/account_banking_mandate_view.xml +++ b/account_banking_mandate/views/account_banking_mandate_view.xml @@ -32,8 +32,11 @@ - + invisible="context.get('mandate_bank_partner_view')" + domain="[('partner_id', '=', partner_id)]" /> + diff --git a/account_banking_pain_base/__init__.py b/account_banking_pain_base/__init__.py index 06e1d7cff..c4f18b657 100644 --- a/account_banking_pain_base/__init__.py +++ b/account_banking_pain_base/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP +# PAIN Base module for Odoo # Copyright (C) 2013 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # diff --git a/account_banking_pain_base/__openerp__.py b/account_banking_pain_base/__openerp__.py index 19aef3e38..3d34a5982 100644 --- a/account_banking_pain_base/__openerp__.py +++ b/account_banking_pain_base/__openerp__.py @@ -1,8 +1,8 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN base module for OpenERP -# Copyright (C) 2013 Akretion (http://www.akretion.com) +# PAIN base module for Odoo +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # # This program is free software: you can redistribute it and/or modify diff --git a/account_banking_pain_base/models/__init__.py b/account_banking_pain_base/models/__init__.py index 22b8d64ae..8a4154482 100644 --- a/account_banking_pain_base/models/__init__.py +++ b/account_banking_pain_base/models/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP +# PAIN Base module for Odoo # Copyright (C) 2013 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # diff --git a/account_banking_pain_base/models/banking_export_pain.py b/account_banking_pain_base/models/banking_export_pain.py index e47d21ff6..82f40e160 100644 --- a/account_banking_pain_base/models/banking_export_pain.py +++ b/account_banking_pain_base/models/banking_export_pain.py @@ -1,8 +1,8 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP -# Copyright (C) 2013 Akretion (http://www.akretion.com) +# PAIN Base module for Odoo +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,8 +20,8 @@ # ############################################################################## -from openerp.osv import orm -from openerp.tools.translate import _ +from openerp import models, api, _ +from openerp.exceptions import Warning from openerp.tools.safe_eval import safe_eval from datetime import datetime from lxml import etree @@ -38,21 +38,19 @@ except ImportError: logger = logging.getLogger(__name__) -class BankingExportPain(orm.AbstractModel): +class BankingExportPain(models.AbstractModel): _name = 'banking.export.pain' - def _validate_iban(self, cr, uid, iban, context=None): + def _validate_iban(self, iban): """if IBAN is valid, returns IBAN if IBAN is NOT valid, raises an error message""" - partner_bank_obj = self.pool.get('res.partner.bank') - if partner_bank_obj.is_iban_valid(cr, uid, iban, context=context): + if self.env['res.partner.bank'].is_iban_valid(iban): return iban.replace(' ', '') else: - raise orm.except_orm( - _('Error:'), _("This IBAN is not valid : %s") % iban) + raise Warning(_("This IBAN is not valid : %s") % iban) - def _prepare_field(self, cr, uid, field_name, field_value, eval_ctx, - max_size=0, gen_args=None, context=None): + def _prepare_field(self, field_name, field_value, eval_ctx, + max_size=0, gen_args=None): """This function is designed to be inherited !""" if gen_args is None: gen_args = {} @@ -74,31 +72,27 @@ class BankingExportPain(orm.AbstractModel): except: line = eval_ctx.get('line') if line: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Cannot compute the '%s' of the Payment Line with " "reference '%s'.") % (field_name, line.name)) else: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Cannot compute the '%s'.") % field_name) if not isinstance(value, (str, unicode)): - raise orm.except_orm( - _('Field type error:'), + raise Warning( _("The type of the field '%s' is %s. It should be a string " "or unicode.") % (field_name, type(value))) if not value: - raise orm.except_orm( - _('Error:'), + raise Warning( _("The '%s' is empty or 0. It should have a non-null value.") % field_name) if max_size and len(value) > max_size: value = value[0:max_size] return value - def _validate_xml(self, cr, uid, xml_string, gen_args, context=None): + def _validate_xml(self, xml_string, gen_args): xsd_etree_obj = etree.parse( tools.file_open(gen_args['pain_xsd_file'])) official_pain_schema = etree.XMLSchema(xsd_etree_obj) @@ -111,19 +105,18 @@ class BankingExportPain(orm.AbstractModel): "The XML file is invalid against the XML Schema Definition") logger.warning(xml_string) logger.warning(e) - raise orm.except_orm( - _('Error:'), + raise Warning( _("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)) + % unicode(e)) return True + @api.multi def finalize_sepa_file_creation( - self, cr, uid, ids, xml_root, total_amount, transactions_count, - gen_args, context=None): + self, xml_root, total_amount, transactions_count, gen_args): xml_string = etree.tostring( xml_root, pretty_print=True, encoding='UTF-8', xml_declaration=True) @@ -131,44 +124,41 @@ class BankingExportPain(orm.AbstractModel): "Generated SEPA XML file in format %s below" % gen_args['pain_flavor']) logger.debug(xml_string) - self._validate_xml(cr, uid, xml_string, gen_args, context=context) + self._validate_xml(xml_string, gen_args) order_ref = [] - for order in gen_args['sepa_export'].payment_order_ids: + for order in self.payment_order_ids: if order.reference: order_ref.append(order.reference.replace('/', '-')) filename = '%s%s.xml' % (gen_args['file_prefix'], '-'.join(order_ref)) - self.write( - cr, uid, ids, { - 'nb_transactions': transactions_count, - 'total_amount': total_amount, - 'filename': filename, - 'file': base64.encodestring(xml_string), - 'state': 'finish', - }, context=context) + self.write({ + 'nb_transactions': transactions_count, + 'total_amount': total_amount, + 'filename': filename, + 'file': base64.encodestring(xml_string), + 'state': 'finish', + }) action = { - 'name': 'SEPA File', + 'name': _('SEPA File'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form,tree', 'res_model': self._name, - 'res_id': ids[0], + 'res_id': self.ids[0], 'target': 'new', } return action - def generate_group_header_block( - self, cr, uid, parent_node, gen_args, context=None): + def generate_group_header_block(self, parent_node, gen_args): group_header_1_0 = etree.SubElement(parent_node, 'GrpHdr') message_identification_1_1 = etree.SubElement( group_header_1_0, 'MsgId') message_identification_1_1.text = self._prepare_field( - cr, uid, 'Message Identification', - 'sepa_export.payment_order_ids[0].reference', - {'sepa_export': gen_args['sepa_export']}, 35, - gen_args=gen_args, context=context) + 'Message Identification', + 'self.payment_order_ids[0].reference', + {'self': self}, 35, gen_args=gen_args) creation_date_time_1_2 = etree.SubElement(group_header_1_0, 'CreDtTm') creation_date_time_1_2.text = datetime.strftime( datetime.today(), '%Y-%m-%dT%H:%M:%S') @@ -176,8 +166,7 @@ class BankingExportPain(orm.AbstractModel): # batch_booking is in "Group header" with pain.001.001.02 # and in "Payment info" in pain.001.001.03/04 batch_booking = etree.SubElement(group_header_1_0, 'BtchBookg') - batch_booking.text = \ - str(gen_args['sepa_export'].batch_booking).lower() + batch_booking.text = unicode(self.batch_booking).lower() nb_of_transactions_1_6 = etree.SubElement( group_header_1_0, 'NbOfTxs') control_sum_1_7 = etree.SubElement(group_header_1_0, 'CtrlSum') @@ -185,30 +174,26 @@ class BankingExportPain(orm.AbstractModel): if gen_args.get('pain_flavor') == 'pain.001.001.02': grouping = etree.SubElement(group_header_1_0, 'Grpg') grouping.text = 'GRPD' - self.generate_initiating_party_block( - cr, uid, group_header_1_0, gen_args, - context=context) + self.generate_initiating_party_block(group_header_1_0, gen_args) return group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 def generate_start_payment_info_block( - self, cr, uid, parent_node, payment_info_ident, + self, parent_node, payment_info_ident, priority, local_instrument, sequence_type, requested_date, - eval_ctx, gen_args, context=None): + eval_ctx, gen_args): payment_info_2_0 = etree.SubElement(parent_node, 'PmtInf') payment_info_identification_2_1 = etree.SubElement( payment_info_2_0, 'PmtInfId') payment_info_identification_2_1.text = self._prepare_field( - cr, uid, 'Payment Information Identification', - payment_info_ident, eval_ctx, 35, - gen_args=gen_args, context=context) + 'Payment Information Identification', + payment_info_ident, eval_ctx, 35, gen_args=gen_args) payment_method_2_2 = etree.SubElement(payment_info_2_0, 'PmtMtd') payment_method_2_2.text = gen_args['payment_method'] nb_of_transactions_2_4 = False control_sum_2_5 = False if gen_args.get('pain_flavor') != 'pain.001.001.02': batch_booking_2_3 = etree.SubElement(payment_info_2_0, 'BtchBookg') - batch_booking_2_3.text = \ - str(gen_args['sepa_export'].batch_booking).lower() + batch_booking_2_3.text = unicode(self.batch_booking).lower() # The "SEPA Customer-to-bank # Implementation guidelines" for SCT and SDD says that control sum # and nb_of_transactions should be present @@ -246,26 +231,24 @@ class BankingExportPain(orm.AbstractModel): requested_date_2_17.text = requested_date return payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 - def _must_have_initiating_party(self, cr, uid, gen_args, context=None): + def _must_have_initiating_party(self, gen_args): '''This method is designed to be inherited in localization modules for countries in which the initiating party is required''' return False - def generate_initiating_party_block( - self, cr, uid, parent_node, gen_args, context=None): + def generate_initiating_party_block(self, parent_node, gen_args): my_company_name = self._prepare_field( - cr, uid, 'Company Name', - 'sepa_export.payment_order_ids[0].mode.bank_id.partner_id.name', - {'sepa_export': gen_args['sepa_export']}, - gen_args.get('name_maxsize'), gen_args=gen_args, context=context) + 'Company Name', + 'self.payment_order_ids[0].mode.bank_id.partner_id.name', + {'self': self}, gen_args.get('name_maxsize'), gen_args=gen_args) initiating_party_1_8 = etree.SubElement(parent_node, 'InitgPty') initiating_party_name = etree.SubElement(initiating_party_1_8, 'Nm') initiating_party_name.text = my_company_name initiating_party_identifier =\ - gen_args['sepa_export'].payment_order_ids[0].company_id.\ + self.payment_order_ids[0].company_id.\ initiating_party_identifier initiating_party_issuer =\ - gen_args['sepa_export'].payment_order_ids[0].company_id.\ + self.payment_order_ids[0].company_id.\ initiating_party_issuer if initiating_party_identifier and initiating_party_issuer: iniparty_id = etree.SubElement(initiating_party_1_8, 'Id') @@ -276,36 +259,33 @@ class BankingExportPain(orm.AbstractModel): iniparty_org_other_issuer = etree.SubElement( iniparty_org_other, 'Issr') iniparty_org_other_issuer.text = initiating_party_issuer - elif self._must_have_initiating_party(cr, uid, gen_args, - context=context): - raise orm.except_orm( - _('Error:'), + elif self._must_have_initiating_party(gen_args): + raise Warning( _("Missing 'Initiating Party Issuer' and/or " "'Initiating Party Identifier' for the company '%s'. " "Both fields must have a value.") - % gen_args['sepa_export'].payment_order_ids[0].company_id.name) + % self.payment_order_ids[0].company_id.name) return True def generate_party_agent( - self, cr, uid, parent_node, party_type, party_type_label, - order, party_name, iban, bic, eval_ctx, gen_args, context=None): + self, parent_node, party_type, party_type_label, + order, party_name, iban, bic, eval_ctx, gen_args): """Generate the piece of the XML file corresponding to BIC This code is mutualized between TRF and DD""" assert order in ('B', 'C'), "Order can be 'B' or 'C'" try: bic = self._prepare_field( - cr, uid, '%s BIC' % party_type_label, bic, eval_ctx, - gen_args=gen_args, context=context) + '%s BIC' % party_type_label, bic, eval_ctx, gen_args=gen_args) party_agent = etree.SubElement(parent_node, '%sAgt' % party_type) party_agent_institution = etree.SubElement( party_agent, 'FinInstnId') party_agent_bic = etree.SubElement( party_agent_institution, gen_args.get('bic_xml_tag')) party_agent_bic.text = bic - except orm.except_orm: + except Warning: if order == 'C': if iban[0:2] != gen_args['initiating_party_country_code']: - raise orm.except_orm( + raise Warning( _('Error:'), _("The bank account with IBAN '%s' of partner '%s' " "must have an associated BIC because it is a " @@ -328,8 +308,8 @@ class BankingExportPain(orm.AbstractModel): return True def generate_party_block( - self, cr, uid, parent_node, party_type, order, name, iban, bic, - eval_ctx, gen_args, context=None): + self, parent_node, party_type, order, name, iban, bic, + eval_ctx, gen_args): """Generate the piece of the XML file corresponding to Name+IBAN+BIC This code is mutualized between TRF and DD""" assert order in ('B', 'C'), "Order can be 'B' or 'C'" @@ -338,23 +318,19 @@ class BankingExportPain(orm.AbstractModel): elif party_type == 'Dbtr': party_type_label = 'Debtor' party_name = self._prepare_field( - cr, uid, '%s Name' % party_type_label, name, eval_ctx, - gen_args.get('name_maxsize'), - gen_args=gen_args, context=context) + '%s Name' % party_type_label, name, eval_ctx, + gen_args.get('name_maxsize'), gen_args=gen_args) piban = self._prepare_field( - cr, uid, '%s IBAN' % party_type_label, iban, eval_ctx, - gen_args=gen_args, - context=context) - viban = self._validate_iban(cr, uid, piban, context=context) + '%s IBAN' % party_type_label, iban, eval_ctx, gen_args=gen_args) + viban = self._validate_iban(piban) # At C level, the order is : BIC, Name, IBAN # At B level, the order is : Name, IBAN, BIC if order == 'B': gen_args['initiating_party_country_code'] = viban[0:2] elif order == 'C': self.generate_party_agent( - cr, uid, parent_node, party_type, party_type_label, - order, party_name, viban, bic, - eval_ctx, gen_args, context=context) + parent_node, party_type, party_type_label, + order, party_name, viban, bic, eval_ctx, gen_args) party = etree.SubElement(parent_node, party_type) party_nm = etree.SubElement(party, 'Nm') party_nm.text = party_name @@ -366,13 +342,11 @@ class BankingExportPain(orm.AbstractModel): party_account_iban.text = viban if order == 'B': self.generate_party_agent( - cr, uid, parent_node, party_type, party_type_label, - order, party_name, viban, bic, - eval_ctx, gen_args, context=context) + parent_node, party_type, party_type_label, + order, party_name, viban, bic, eval_ctx, gen_args) return True - def generate_remittance_info_block( - self, cr, uid, parent_node, line, gen_args, context=None): + def generate_remittance_info_block(self, parent_node, line, gen_args): remittance_info_2_91 = etree.SubElement( parent_node, 'RmtInf') @@ -381,14 +355,12 @@ class BankingExportPain(orm.AbstractModel): remittance_info_2_91, 'Ustrd') remittance_info_unstructured_2_99.text = \ self._prepare_field( - cr, uid, 'Remittance Unstructured Information', + 'Remittance Unstructured Information', 'line.communication', {'line': line}, 140, - gen_args=gen_args, - context=context) + gen_args=gen_args) else: if not line.struct_communication_type: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Missing 'Structured Communication Type' on payment " "line with reference '%s'.") % line.name) @@ -422,22 +394,20 @@ class BankingExportPain(orm.AbstractModel): line.struct_communication_type creditor_reference_2_126.text = \ self._prepare_field( - cr, uid, 'Creditor Structured Reference', + 'Creditor Structured Reference', 'line.communication', {'line': line}, 35, - gen_args=gen_args, - context=context) + gen_args=gen_args) return True def generate_creditor_scheme_identification( - self, cr, uid, parent_node, identification, identification_label, - eval_ctx, scheme_name_proprietary, gen_args, context=None): + self, parent_node, identification, identification_label, + eval_ctx, scheme_name_proprietary, gen_args): csi_id = etree.SubElement(parent_node, 'Id') csi_privateid = etree.SubElement(csi_id, 'PrvtId') csi_other = etree.SubElement(csi_privateid, 'Othr') csi_other_id = etree.SubElement(csi_other, 'Id') csi_other_id.text = self._prepare_field( - cr, uid, identification_label, identification, eval_ctx, - gen_args=gen_args, context=context) + identification_label, identification, eval_ctx, gen_args=gen_args) csi_scheme_name = etree.SubElement(csi_other, 'SchmeNm') csi_scheme_name_proprietary = etree.SubElement( csi_scheme_name, 'Prtry') diff --git a/account_banking_pain_base/models/payment_line.py b/account_banking_pain_base/models/payment_line.py index 872827b7b..9717fd562 100644 --- a/account_banking_pain_base/models/payment_line.py +++ b/account_banking_pain_base/models/payment_line.py @@ -1,8 +1,8 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP -# Copyright (C) 2013 Akretion (http://www.akretion.com) +# PAIN Base module for Odoo +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,33 +20,23 @@ # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class PaymentLine(orm.Model): +class PaymentLine(models.Model): _inherit = 'payment.line' - def _get_struct_communication_types(self, cr, uid, context=None): + def _get_struct_communication_types(self): return [('ISO', 'ISO')] - _columns = { - 'priority': fields.selection( - [('NORM', 'Normal'), - ('HIGH', 'High')], 'Priority', - help="This field will be used as the 'Instruction Priority' in " - "the generated PAIN file."), - # Update size from 64 to 140, because PAIN allows 140 caracters - 'communication': fields.char( - 'Communication', size=140, required=True, - help="Used as the message between ordering customer and current " - "company. Depicts 'What do you want to say to the recipient " - "about this order ?'"), - 'struct_communication_type': fields.selection( - '_get_struct_communication_types', - 'Structured Communication Type'), - } - - _defaults = { - 'priority': 'NORM', - 'struct_communication_type': 'ISO', - } + priority = fields.Selection([ + ('NORM', 'Normal'), + ('HIGH', 'High')], + string='Priority', default='NORM', + help="This field will be used as the 'Instruction Priority' in " + "the generated PAIN file.") + # Update size from 64 to 140, because PAIN allows 140 caracters + communication = fields.Char(size=140) + struct_communication_type = fields.Selection( + '_get_struct_communication_types', + string='Structured Communication Type', default='ISO') diff --git a/account_banking_pain_base/models/payment_mode.py b/account_banking_pain_base/models/payment_mode.py index ff9254ca0..9ca025e27 100644 --- a/account_banking_pain_base/models/payment_mode.py +++ b/account_banking_pain_base/models/payment_mode.py @@ -1,8 +1,8 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP -# Copyright (C) 2013 Akretion (http://www.akretion.com) +# PAIN Base module for Odoo +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # # This program is free software: you can redistribute it and/or modify @@ -20,20 +20,14 @@ # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class payment_mode(orm.Model): +class PaymentMode(models.Model): _inherit = 'payment.mode' - _columns = { - 'convert_to_ascii': fields.boolean( - 'Convert to ASCII', - help="If active, Odoo will convert each accented caracter to " - "the corresponding unaccented caracter, so that only ASCII " - "caracters are used in the generated PAIN file."), - } - - _defaults = { - 'convert_to_ascii': True, - } + convert_to_ascii = fields.Boolean( + string='Convert to ASCII', default=True, + help="If active, Odoo will convert each accented caracter to " + "the corresponding unaccented caracter, so that only ASCII " + "caracters are used in the generated PAIN file.") diff --git a/account_banking_pain_base/models/res_company.py b/account_banking_pain_base/models/res_company.py index bb656b55a..0a62caab9 100644 --- a/account_banking_pain_base/models/res_company.py +++ b/account_banking_pain_base/models/res_company.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# PAIN Base module for OpenERP +# PAIN Base module for Odoo # Copyright (C) 2013-2015 Akretion (http://www.akretion.com) # Copyright (C) 2013 Noviat (http://www.noviat.com) # @author: Alexis de Lattre diff --git a/account_banking_payment_export/__openerp__.py b/account_banking_payment_export/__openerp__.py index 3b8b7eadf..a91bd5d29 100644 --- a/account_banking_payment_export/__openerp__.py +++ b/account_banking_payment_export/__openerp__.py @@ -44,6 +44,7 @@ 'wizard/bank_payment_manual.xml', 'wizard/payment_order_create_view.xml', 'data/payment_mode_type.xml', + 'workflow/account_payment.xml', 'security/ir.model.access.csv', ], 'demo': ['demo/banking_demo.xml'], diff --git a/account_banking_payment_export/models/account_payment.py b/account_banking_payment_export/models/account_payment.py index 3ab44b586..59f5a1da7 100644 --- a/account_banking_payment_export/models/account_payment.py +++ b/account_banking_payment_export/models/account_payment.py @@ -73,3 +73,11 @@ class PaymentOrder(models.Model): workflow.trg_validate(self.env.uid, 'payment.order', order_id, 'done', self.env.cr) return {} + + @api.multi + def action_done(self): + self.write({ + 'date_done': fields.Date.context_today(self), + 'state': 'done', + }) + return True diff --git a/account_banking_payment_export/workflow/account_payment.xml b/account_banking_payment_export/workflow/account_payment.xml new file mode 100644 index 000000000..ac5cf4a79 --- /dev/null +++ b/account_banking_payment_export/workflow/account_payment.xml @@ -0,0 +1,16 @@ + + + + + + + + action_done() + + + + diff --git a/account_banking_payment_transfer/__init__.py b/account_banking_payment_transfer/__init__.py index 9186ee3ad..9180c0e2e 100644 --- a/account_banking_payment_transfer/__init__.py +++ b/account_banking_payment_transfer/__init__.py @@ -1 +1,2 @@ from . import model +from .post_install import set_date_sent diff --git a/account_banking_payment_transfer/__openerp__.py b/account_banking_payment_transfer/__openerp__.py index e9d37c1d6..88fee1af1 100644 --- a/account_banking_payment_transfer/__openerp__.py +++ b/account_banking_payment_transfer/__openerp__.py @@ -31,6 +31,7 @@ 'author': "Banking addons community,Odoo Community Association (OCA)", 'website': 'https://github.com/OCA/banking', 'category': 'Banking addons', + 'post_init_hook': 'set_date_sent', 'depends': [ 'account_banking_payment_export', ], diff --git a/account_banking_payment_transfer/model/account_payment.py b/account_banking_payment_transfer/model/account_payment.py index 4199c90e3..daeb1874b 100644 --- a/account_banking_payment_transfer/model/account_payment.py +++ b/account_banking_payment_transfer/model/account_payment.py @@ -98,9 +98,7 @@ class PaymentOrder(models.Model): def action_done(self): for line in self.line_ids: line.date_done = fields.Date.context_today(self) - self.date_done = fields.Date.context_today(self) - # state is written in workflow definition - return True + return super(PaymentOrder, self).action_done() @api.multi def _get_transfer_move_lines(self): diff --git a/account_banking_payment_transfer/post_install.py b/account_banking_payment_transfer/post_install.py new file mode 100644 index 000000000..9d23444f9 --- /dev/null +++ b/account_banking_payment_transfer/post_install.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Account Banking Payment Transfer module for Odoo +# Copyright (C) 2015 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 . +# +############################################################################## + + +def set_date_sent(cr, pool): + cr.execute('UPDATE payment_order set date_sent=date_done') diff --git a/account_banking_payment_transfer/workflow/account_payment.xml b/account_banking_payment_transfer/workflow/account_payment.xml index 0a208b317..ec49fa56b 100644 --- a/account_banking_payment_transfer/workflow/account_payment.xml +++ b/account_banking_payment_transfer/workflow/account_payment.xml @@ -68,8 +68,6 @@ write({'state':'rejected'}) unfortunately. --> - action_done() -write({'state':'done'}) diff --git a/account_banking_sepa_credit_transfer/wizard/__init__.py b/account_banking_sepa_credit_transfer/wizard/__init__.py index 05230383b..399f6ef5c 100644 --- a/account_banking_sepa_credit_transfer/wizard/__init__.py +++ b/account_banking_sepa_credit_transfer/wizard/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# SEPA Credit Transfer module for OpenERP +# SEPA Credit Transfer module for Odoo # Copyright (C) 2010-2013 Akretion (http://www.akretion.com) # @author: Alexis de Lattre # diff --git a/account_banking_sepa_credit_transfer/wizard/export_sepa.py b/account_banking_sepa_credit_transfer/wizard/export_sepa.py index 100e626e8..8ea02de2f 100644 --- a/account_banking_sepa_credit_transfer/wizard/export_sepa.py +++ b/account_banking_sepa_credit_transfer/wizard/export_sepa.py @@ -21,75 +21,65 @@ ############################################################################## -from openerp.osv import orm, fields -from openerp.tools.translate import _ +from openerp import models, fields, api, _ +from openerp.exceptions import Warning from openerp import workflow from lxml import etree -class BankingExportSepaWizard(orm.TransientModel): +class BankingExportSepaWizard(models.TransientModel): _name = 'banking.export.sepa.wizard' _inherit = ['banking.export.pain'] _description = 'Export SEPA Credit Transfer File' - _columns = { - 'state': fields.selection([('create', 'Create'), - ('finish', 'Finish')], 'State', - readonly=True), - 'batch_booking': fields.boolean( - 'Batch Booking', - help="If true, the bank statement will display only one debit " - "line for all the wire transfers of the SEPA XML file ; if " - "false, the bank statement will display one debit line per wire " - "transfer of the SEPA XML file."), - 'charge_bearer': fields.selection( - [('SLEV', 'Following Service Level'), - ('SHAR', 'Shared'), - ('CRED', 'Borne by Creditor'), - ('DEBT', 'Borne by Debtor')], 'Charge Bearer', required=True, - help="Following service level : transaction charges are to be " - "applied following the rules agreed in the service level " - "and/or scheme (SEPA Core messages must use this). Shared : " - "transaction charges on the debtor side are to be borne by " - "the debtor, transaction charges on the creditor side are to " - "be borne by the creditor. Borne by creditor : all " - "transaction charges are to be borne by the creditor. Borne " - "by debtor : all transaction charges are to be borne by the " - "debtor."), - 'nb_transactions': fields.integer( - string='Number of Transactions', readonly=True), - 'total_amount': fields.float( - string='Total Amount', readonly=True), - 'file': fields.binary( - string="File", readonly=True), - 'filename': fields.char( - string="Filename", readonly=True), - 'payment_order_ids': fields.many2many( - 'payment.order', 'wiz_sepa_payorders_rel', 'wizard_id', - 'payment_order_id', 'Payment Orders', readonly=True), - } + state = fields.Selection([ + ('create', 'Create'), + ('finish', 'Finish')], + string='State', readonly=True, default='create') + batch_booking = fields.Boolean( + string='Batch Booking', + help="If true, the bank statement will display only one debit " + "line for all the wire transfers of the SEPA XML file ; if " + "false, the bank statement will display one debit line per wire " + "transfer of the SEPA XML file.") + charge_bearer = fields.Selection([ + ('SLEV', 'Following Service Level'), + ('SHAR', 'Shared'), + ('CRED', 'Borne by Creditor'), + ('DEBT', 'Borne by Debtor')], string='Charge Bearer', + default='SLEV', required=True, + help="Following service level : transaction charges are to be " + "applied following the rules agreed in the service level " + "and/or scheme (SEPA Core messages must use this). Shared : " + "transaction charges on the debtor side are to be borne by " + "the debtor, transaction charges on the creditor side are to " + "be borne by the creditor. Borne by creditor : all " + "transaction charges are to be borne by the creditor. Borne " + "by debtor : all transaction charges are to be borne by the " + "debtor.") + nb_transactions = fields.Integer( + string='Number of Transactions', readonly=True) + total_amount = fields.Float(string='Total Amount', readonly=True) + file = fields.Binary(string="File", readonly=True) + filename = fields.Char(string="Filename", readonly=True) + payment_order_ids = fields.Many2many( + 'payment.order', 'wiz_sepa_payorders_rel', 'wizard_id', + 'payment_order_id', string='Payment Orders', readonly=True) - _defaults = { - 'charge_bearer': 'SLEV', - 'state': 'create', - } - - def create(self, cr, uid, vals, context=None): - payment_order_ids = context.get('active_ids', []) + @api.model + def create(self, vals): + payment_order_ids = self._context.get('active_ids', []) vals.update({ 'payment_order_ids': [[6, 0, payment_order_ids]], }) - return super(BankingExportSepaWizard, self).create( - cr, uid, vals, context=context) + return super(BankingExportSepaWizard, self).create(vals) - def create_sepa(self, cr, uid, ids, context=None): + @api.multi + def create_sepa(self): """Creates the SEPA Credit Transfer file. That's the important code!""" - if context is None: - context = {} - sepa_export = self.browse(cr, uid, ids[0], context=context) - pain_flavor = sepa_export.payment_order_ids[0].mode.type.code + pain_flavor = self.payment_order_ids[0].mode.type.code convert_to_ascii = \ - sepa_export.payment_order_ids[0].mode.convert_to_ascii + self.payment_order_ids[0].mode.convert_to_ascii if pain_flavor == 'pain.001.001.02': bic_xml_tag = 'BIC' name_maxsize = 70 @@ -115,8 +105,7 @@ class BankingExportSepaWizard(orm.TransientModel): name_maxsize = 140 root_xml_tag = 'CstmrCdtTrfInitn' else: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Payment Type Code '%s' is not supported. The only " "Payment Type Codes supported for SEPA Credit Transfers " "are 'pain.001.001.02', 'pain.001.001.03', " @@ -129,7 +118,6 @@ class BankingExportSepaWizard(orm.TransientModel): 'payment_method': 'TRF', 'file_prefix': 'sct_', 'pain_flavor': pain_flavor, - 'sepa_export': sepa_export, 'pain_xsd_file': 'account_banking_sepa_credit_transfer/data/%s.xsd' % pain_flavor, @@ -144,16 +132,15 @@ class BankingExportSepaWizard(orm.TransientModel): ['pain.001.001.03', 'pain.001.001.04', 'pain.001.001.05'] # A. Group header group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \ - self.generate_group_header_block( - cr, uid, pain_root, gen_args, context=context) + self.generate_group_header_block(pain_root, gen_args) transactions_count_1_6 = 0 total_amount = 0.0 amount_control_sum_1_7 = 0.0 lines_per_group = {} # key = (requested_date, priority) # values = list of lines as object - today = fields.date.context_today(self, cr, uid, context=context) - for payment_order in sepa_export.payment_order_ids: + today = fields.Date.context_today(self) + for payment_order in self.payment_order_ids: total_amount = total_amount + payment_order.total for line in payment_order.line_ids: priority = line.priority @@ -170,32 +157,30 @@ class BankingExportSepaWizard(orm.TransientModel): lines_per_group[key] = [line] # Write requested_date on 'Payment date' of the pay line if requested_date != line.date: - self.pool['payment.line'].write( - cr, uid, line.id, - {'date': requested_date}, context=context) + line.date = requested_date for (requested_date, priority), lines in lines_per_group.items(): # B. Payment info payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 = \ self.generate_start_payment_info_block( - cr, uid, pain_root, - "sepa_export.payment_order_ids[0].reference + '-' " + pain_root, + "self.payment_order_ids[0].reference + '-' " "+ requested_date.replace('-', '') + '-' + priority", priority, False, False, requested_date, { - 'sepa_export': sepa_export, + 'self': self, 'priority': priority, 'requested_date': requested_date, - }, gen_args, context=context) + }, gen_args) self.generate_party_block( - cr, uid, payment_info_2_0, 'Dbtr', 'B', - 'sepa_export.payment_order_ids[0].mode.bank_id.partner_id.' + payment_info_2_0, 'Dbtr', 'B', + 'self.payment_order_ids[0].mode.bank_id.partner_id.' 'name', - 'sepa_export.payment_order_ids[0].mode.bank_id.acc_number', - 'sepa_export.payment_order_ids[0].mode.bank_id.bank.bic or ' - 'sepa_export.payment_order_ids[0].mode.bank_id.bank_bic', - {'sepa_export': sepa_export}, - gen_args, context=context) + 'self.payment_order_ids[0].mode.bank_id.acc_number', + 'self.payment_order_ids[0].mode.bank_id.bank.bic or ' + 'self.payment_order_ids[0].mode.bank_id.bank_bic', + {'self': self}, + gen_args) charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr') - charge_bearer_2_24.text = sepa_export.charge_bearer + charge_bearer_2_24.text = self.charge_bearer transactions_count_2_4 = 0 amount_control_sum_2_5 = 0.0 for line in lines: @@ -209,13 +194,11 @@ class BankingExportSepaWizard(orm.TransientModel): end2end_identification_2_30 = etree.SubElement( payment_identification_2_28, 'EndToEndId') end2end_identification_2_30.text = self._prepare_field( - cr, uid, 'End to End Identification', 'line.name', - {'line': line}, 35, gen_args=gen_args, - context=context) + 'End to End Identification', 'line.name', + {'line': line}, 35, gen_args=gen_args) currency_name = self._prepare_field( - cr, uid, 'Currency Code', 'line.currency.name', - {'line': line}, 3, gen_args=gen_args, - context=context) + 'Currency Code', 'line.currency.name', + {'line': line}, 3, gen_args=gen_args) amount_2_42 = etree.SubElement( credit_transfer_transaction_info_2_27, 'Amt') instructed_amount_2_43 = etree.SubElement( @@ -224,48 +207,44 @@ class BankingExportSepaWizard(orm.TransientModel): amount_control_sum_1_7 += line.amount_currency amount_control_sum_2_5 += line.amount_currency if not line.bank_id: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Missing Bank Account on invoice '%s' (payment " "order line reference '%s').") % (line.ml_inv_ref.number, line.name)) self.generate_party_block( - cr, uid, credit_transfer_transaction_info_2_27, 'Cdtr', + credit_transfer_transaction_info_2_27, 'Cdtr', 'C', 'line.partner_id.name', 'line.bank_id.acc_number', 'line.bank_id.bank.bic or ' - 'line.bank_id.bank_bic', {'line': line}, gen_args, - context=context) + 'line.bank_id.bank_bic', {'line': line}, gen_args) self.generate_remittance_info_block( - cr, uid, credit_transfer_transaction_info_2_27, - line, gen_args, context=context) + credit_transfer_transaction_info_2_27, line, gen_args) if pain_flavor in pain_03_to_05: - nb_of_transactions_2_4.text = str(transactions_count_2_4) + nb_of_transactions_2_4.text = unicode(transactions_count_2_4) control_sum_2_5.text = '%.2f' % amount_control_sum_2_5 if pain_flavor in pain_03_to_05: - nb_of_transactions_1_6.text = str(transactions_count_1_6) + nb_of_transactions_1_6.text = unicode(transactions_count_1_6) control_sum_1_7.text = '%.2f' % amount_control_sum_1_7 else: - nb_of_transactions_1_6.text = str(transactions_count_1_6) + nb_of_transactions_1_6.text = unicode(transactions_count_1_6) control_sum_1_7.text = '%.2f' % amount_control_sum_1_7 return self.finalize_sepa_file_creation( - cr, uid, ids, xml_root, total_amount, transactions_count_1_6, - gen_args, context=context) + xml_root, total_amount, transactions_count_1_6, gen_args) - def save_sepa(self, cr, uid, ids, context=None): + @api.multi + def save_sepa(self): """Save the SEPA file: send the done signal to all payment orders in the file. With the default workflow, they will transition to 'done', while with the advanced workflow in account_banking_payment they will transition to 'sent' waiting reconciliation. """ - sepa_export = self.browse(cr, uid, ids[0], context=context) - for order in sepa_export.payment_order_ids: - workflow.trg_validate(uid, 'payment.order', order.id, 'done', cr) - self.pool['ir.attachment'].create( - cr, uid, { - 'res_model': 'payment.order', - 'res_id': order.id, - 'name': sepa_export.filename, - 'datas': sepa_export.file, - }, context=context) + for order in self.payment_order_ids: + workflow.trg_validate( + self._uid, 'payment.order', order.id, 'done', self._cr) + self.env['ir.attachment'].create({ + 'res_model': 'payment.order', + 'res_id': order.id, + 'name': self.filename, + 'datas': self.file, + }) return True diff --git a/account_banking_sepa_direct_debit/wizard/export_sdd.py b/account_banking_sepa_direct_debit/wizard/export_sdd.py index 9fc1e2876..2c0d93d57 100644 --- a/account_banking_sepa_direct_debit/wizard/export_sdd.py +++ b/account_banking_sepa_direct_debit/wizard/export_sdd.py @@ -21,99 +21,85 @@ ############################################################################## -from openerp.osv import orm, fields -from openerp.tools.translate import _ +from openerp import models, fields, api, _ +from openerp.exceptions import Warning from openerp import workflow from lxml import etree -class BankingExportSddWizard(orm.TransientModel): +class BankingExportSddWizard(models.TransientModel): _name = 'banking.export.sdd.wizard' _inherit = ['banking.export.pain'] _description = 'Export SEPA Direct Debit File' - _columns = { - 'state': fields.selection( - [('create', 'Create'), - ('finish', 'Finish')], 'State', readonly=True), - 'batch_booking': fields.boolean( - 'Batch Booking', - help="If true, the bank statement will display only one credit " - "line for all the direct debits of the SEPA file ; if false, " - "the bank statement will display one credit line per direct " - "debit of the SEPA file."), - 'charge_bearer': fields.selection( - [('SLEV', 'Following Service Level'), - ('SHAR', 'Shared'), - ('CRED', 'Borne by Creditor'), - ('DEBT', 'Borne by Debtor')], 'Charge Bearer', required=True, - help="Following service level : transaction charges are to be " - "applied following the rules agreed in the service level " - "and/or scheme (SEPA Core messages must use this). Shared : " - "transaction charges on the creditor side are to be borne " - "by the creditor, transaction charges on the debtor side are " - "to be borne by the debtor. Borne by creditor : all " - "transaction charges are to be borne by the creditor. Borne " - "by debtor : all transaction charges are to be borne by the " - "debtor."), - 'nb_transactions': fields.integer( - string='Number of Transactions', readonly=True), - 'total_amount': fields.float( - string='Total Amount', readonly=True), - 'file': fields.binary( - string="File", readonly=True), - 'filename': fields.char( - string="Filename", readonly=True), - 'payment_order_ids': fields.many2many( - 'payment.order', 'wiz_sdd_payorders_rel', 'wizard_id', - 'payment_order_id', 'Payment Orders', readonly=True), - } - _defaults = { - 'charge_bearer': 'SLEV', - 'state': 'create', - } + state = fields.Selection([ + ('create', 'Create'), + ('finish', 'Finish'), + ], string='State', readonly=True, default='create') + batch_booking = fields.Boolean( + string='Batch Booking', + help="If true, the bank statement will display only one credit " + "line for all the direct debits of the SEPA file ; if false, " + "the bank statement will display one credit line per direct " + "debit of the SEPA file.") + charge_bearer = fields.Selection([ + ('SLEV', 'Following Service Level'), + ('SHAR', 'Shared'), + ('CRED', 'Borne by Creditor'), + ('DEBT', 'Borne by Debtor'), + ], string='Charge Bearer', required=True, default='SLEV', + help="Following service level : transaction charges are to be " + "applied following the rules agreed in the service level " + "and/or scheme (SEPA Core messages must use this). Shared : " + "transaction charges on the creditor side are to be borne " + "by the creditor, transaction charges on the debtor side are " + "to be borne by the debtor. Borne by creditor : all " + "transaction charges are to be borne by the creditor. Borne " + "by debtor : all transaction charges are to be borne by the debtor.") + nb_transactions = fields.Integer( + string='Number of Transactions', readonly=True) + total_amount = fields.Float(string='Total Amount', readonly=True) + file = fields.Binary(string="File", readonly=True) + filename = fields.Char(string="Filename", readonly=True) + payment_order_ids = fields.Many2many( + 'payment.order', 'wiz_sdd_payorders_rel', 'wizard_id', + 'payment_order_id', string='Payment Orders', readonly=True) - def create(self, cr, uid, vals, context=None): - payment_order_ids = context.get('active_ids', []) + @api.model + def create(self, vals): + payment_order_ids = self._context.get('active_ids', []) vals.update({ 'payment_order_ids': [[6, 0, payment_order_ids]], }) - return super(BankingExportSddWizard, self).create( - cr, uid, vals, context=context) + return super(BankingExportSddWizard, self).create(vals) - def _get_previous_bank(self, cr, uid, payline, context=None): - payline_obj = self.pool['payment.line'] + def _get_previous_bank(self, payline): previous_bank = False - payline_ids = payline_obj.search( - cr, uid, [ - ('mandate_id', '=', payline.mandate_id.id), - ('bank_id', '!=', payline.bank_id.id), - ], - context=context) - if payline_ids: - older_lines = payline_obj.browse( - cr, uid, payline_ids, context=context) + older_lines = self.env['payment.line'].search([ + ('mandate_id', '=', payline.mandate_id.id), + ('bank_id', '!=', payline.bank_id.id)]) + if older_lines: previous_date = False - previous_payline_id = False + previous_payline = False for older_line in older_lines: - older_line_date_sent = older_line.order_id.date_sent - if (older_line_date_sent and - older_line_date_sent > previous_date): - previous_date = older_line_date_sent - previous_payline_id = older_line.id - if previous_payline_id: - previous_payline = payline_obj.browse( - cr, uid, previous_payline_id, context=context) + if hasattr(older_line.order_id, 'date_sent'): + older_line_date = older_line.order_id.date_sent + else: + older_line_date = older_line.order_id.date_done + if (older_line_date and + older_line_date > previous_date): + previous_date = older_line_date + previous_payline = older_line + if previous_payline: previous_bank = previous_payline.bank_id return previous_bank - def create_sepa(self, cr, uid, ids, context=None): + @api.multi + def create_sepa(self): """Creates the SEPA Direct Debit file. That's the important code !""" - sepa_export = self.browse(cr, uid, ids[0], context=context) - - pain_flavor = sepa_export.payment_order_ids[0].mode.type.code + pain_flavor = self.payment_order_ids[0].mode.type.code convert_to_ascii = \ - sepa_export.payment_order_ids[0].mode.convert_to_ascii + self.payment_order_ids[0].mode.convert_to_ascii if pain_flavor == 'pain.008.001.02': bic_xml_tag = 'BIC' name_maxsize = 70 @@ -127,8 +113,7 @@ class BankingExportSddWizard(orm.TransientModel): name_maxsize = 140 root_xml_tag = 'CstmrDrctDbtInitn' else: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Payment Type Code '%s' is not supported. The only " "Payment Type Code supported for SEPA Direct Debit are " "'pain.008.001.02', 'pain.008.001.03' and " @@ -140,7 +125,6 @@ class BankingExportSddWizard(orm.TransientModel): 'payment_method': 'DD', 'file_prefix': 'sdd_', 'pain_flavor': pain_flavor, - 'sepa_export': sepa_export, 'pain_xsd_file': 'account_banking_sepa_direct_debit/data/%s.xsd' % pain_flavor, } @@ -152,8 +136,7 @@ class BankingExportSddWizard(orm.TransientModel): pain_root = etree.SubElement(xml_root, root_xml_tag) # A. Group header group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \ - self.generate_group_header_block( - cr, uid, pain_root, gen_args, context=context) + self.generate_group_header_block(pain_root, gen_args) transactions_count_1_6 = 0 total_amount = 0.0 amount_control_sum_1_7 = 0.0 @@ -161,8 +144,8 @@ class BankingExportSddWizard(orm.TransientModel): # key = (requested_date, priority, sequence type) # value = list of lines as objects # Iterate on payment orders - today = fields.date.context_today(self, cr, uid, context=context) - for payment_order in sepa_export.payment_order_ids: + today = fields.Date.context_today(self) + for payment_order in self.payment_order_ids: total_amount = total_amount + payment_order.total # Iterate each payment lines for line in payment_order.line_ids: @@ -175,16 +158,14 @@ class BankingExportSddWizard(orm.TransientModel): else: requested_date = today if not line.mandate_id: - raise orm.except_orm( - _('Error:'), + raise Warning( _("Missing SEPA Direct Debit mandate on the payment " "line with partner '%s' and Invoice ref '%s'.") % (line.partner_id.name, line.ml_inv_ref.number)) scheme = line.mandate_id.scheme if line.mandate_id.state != 'valid': - raise orm.except_orm( - _('Error:'), + raise Warning( _("The SEPA Direct Debit mandate with reference '%s' " "for partner '%s' has expired.") % (line.mandate_id.unique_mandate_reference, @@ -192,8 +173,7 @@ class BankingExportSddWizard(orm.TransientModel): if line.mandate_id.type == 'oneoff': seq_type = 'OOFF' if line.mandate_id.last_debit_date: - raise orm.except_orm( - _('Error:'), + raise Warning( _("The mandate with reference '%s' for partner " "'%s' has type set to 'One-Off' and it has a " "last debit date set to '%s', so we can't use " @@ -218,45 +198,41 @@ class BankingExportSddWizard(orm.TransientModel): lines_per_group[key] = [line] # Write requested_exec_date on 'Payment date' of the pay line if requested_date != line.date: - self.pool['payment.line'].write( - cr, uid, line.id, - {'date': requested_date}, context=context) + line.date = requested_date for (requested_date, priority, sequence_type, scheme), lines in \ lines_per_group.items(): # B. Payment info payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 = \ self.generate_start_payment_info_block( - cr, uid, pain_root, - "sepa_export.payment_order_ids[0].reference + '-' + " + pain_root, + "self.payment_order_ids[0].reference + '-' + " "sequence_type + '-' + requested_date.replace('-', '') " "+ '-' + priority", priority, scheme, sequence_type, requested_date, { - 'sepa_export': sepa_export, + 'self': self, 'sequence_type': sequence_type, 'priority': priority, 'requested_date': requested_date, - }, gen_args, context=context) + }, gen_args) self.generate_party_block( - cr, uid, payment_info_2_0, 'Cdtr', 'B', - 'sepa_export.payment_order_ids[0].mode.bank_id.partner_id.' + payment_info_2_0, 'Cdtr', 'B', + 'self.payment_order_ids[0].mode.bank_id.partner_id.' 'name', - 'sepa_export.payment_order_ids[0].mode.bank_id.acc_number', - 'sepa_export.payment_order_ids[0].mode.bank_id.bank.bic or ' - 'sepa_export.payment_order_ids[0].mode.bank_id.bank_bic', - {'sepa_export': sepa_export}, - gen_args, context=context) + 'self.payment_order_ids[0].mode.bank_id.acc_number', + 'self.payment_order_ids[0].mode.bank_id.bank.bic or ' + 'self.payment_order_ids[0].mode.bank_id.bank_bic', + {'self': self}, gen_args) charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr') - charge_bearer_2_24.text = sepa_export.charge_bearer + charge_bearer_2_24.text = self.charge_bearer creditor_scheme_identification_2_27 = etree.SubElement( payment_info_2_0, 'CdtrSchmeId') self.generate_creditor_scheme_identification( - cr, uid, creditor_scheme_identification_2_27, - 'sepa_export.payment_order_ids[0].company_id.' + creditor_scheme_identification_2_27, + 'self.payment_order_ids[0].company_id.' 'sepa_creditor_identifier', - 'SEPA Creditor Identifier', {'sepa_export': sepa_export}, - 'SEPA', gen_args, context=context) + 'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args) transactions_count_2_4 = 0 amount_control_sum_2_5 = 0.0 for line in lines: @@ -269,13 +245,11 @@ class BankingExportSddWizard(orm.TransientModel): end2end_identification_2_31 = etree.SubElement( payment_identification_2_29, 'EndToEndId') end2end_identification_2_31.text = self._prepare_field( - cr, uid, 'End to End Identification', 'line.name', - {'line': line}, 35, - gen_args=gen_args, context=context) + 'End to End Identification', 'line.name', + {'line': line}, 35, gen_args=gen_args) currency_name = self._prepare_field( - cr, uid, 'Currency Code', 'line.currency.name', - {'line': line}, 3, gen_args=gen_args, - context=context) + 'Currency Code', 'line.currency.name', + {'line': line}, 3, gen_args=gen_args) instructed_amount_2_44 = etree.SubElement( dd_transaction_info_2_28, 'InstdAmt', Ccy=currency_name) instructed_amount_2_44.text = '%.2f' % line.amount_currency @@ -288,22 +262,19 @@ class BankingExportSddWizard(orm.TransientModel): mandate_identification_2_48 = etree.SubElement( mandate_related_info_2_47, 'MndtId') mandate_identification_2_48.text = self._prepare_field( - cr, uid, 'Unique Mandate Reference', + 'Unique Mandate Reference', 'line.mandate_id.unique_mandate_reference', - {'line': line}, 35, - gen_args=gen_args, context=context) + {'line': line}, 35, gen_args=gen_args) mandate_signature_date_2_49 = etree.SubElement( mandate_related_info_2_47, 'DtOfSgntr') mandate_signature_date_2_49.text = self._prepare_field( - cr, uid, 'Mandate Signature Date', + 'Mandate Signature Date', 'line.mandate_id.signature_date', - {'line': line}, 10, - gen_args=gen_args, context=context) + {'line': line}, 10, gen_args=gen_args) if sequence_type == 'FRST' and ( line.mandate_id.last_debit_date or not line.mandate_id.sepa_migrated): - previous_bank = self._get_previous_bank( - cr, uid, line, context=context) + previous_bank = self._get_previous_bank(line) if previous_bank or not line.mandate_id.sepa_migrated: amendment_indicator_2_50 = etree.SubElement( mandate_related_info_2_47, 'AmdmntInd') @@ -322,13 +293,11 @@ class BankingExportSddWizard(orm.TransientModel): ori_debtor_account_iban = etree.SubElement( ori_debtor_account_id, 'IBAN') ori_debtor_account_iban.text = self._validate_iban( - cr, uid, self._prepare_field( - cr, uid, 'Original Debtor Account', + self._prepare_field( + 'Original Debtor Account', 'previous_bank.acc_number', {'previous_bank': previous_bank}, - gen_args=gen_args, - context=context), - context=context) + gen_args=gen_args)) else: ori_debtor_agent_2_58 = etree.SubElement( amendment_info_details_2_51, 'OrgnlDbtrAgt') @@ -337,12 +306,11 @@ class BankingExportSddWizard(orm.TransientModel): ori_debtor_agent_bic = etree.SubElement( ori_debtor_agent_institution, bic_xml_tag) ori_debtor_agent_bic.text = self._prepare_field( - cr, uid, 'Original Debtor Agent', + 'Original Debtor Agent', 'previous_bank.bank.bic or ' 'previous_bank.bank_bic', {'previous_bank': previous_bank}, - gen_args=gen_args, - context=context) + gen_args=gen_args) ori_debtor_agent_other = etree.SubElement( ori_debtor_agent_institution, 'Othr') ori_debtor_agent_other_id = etree.SubElement( @@ -354,74 +322,73 @@ class BankingExportSddWizard(orm.TransientModel): amendment_info_details_2_51, 'OrgnlMndtId') ori_mandate_identification_2_52.text = \ self._prepare_field( - cr, uid, 'Original Mandate Identification', + 'Original Mandate Identification', 'line.mandate_id.' 'original_mandate_identification', {'line': line}, - gen_args=gen_args, - context=context) + gen_args=gen_args) ori_creditor_scheme_id_2_53 = etree.SubElement( amendment_info_details_2_51, 'OrgnlCdtrSchmeId') self.generate_creditor_scheme_identification( - cr, uid, ori_creditor_scheme_id_2_53, - 'sepa_export.payment_order_ids[0].company_id.' + ori_creditor_scheme_id_2_53, + 'self.payment_order_ids[0].company_id.' 'original_creditor_identifier', 'Original Creditor Identifier', - {'sepa_export': sepa_export}, - 'SEPA', gen_args, context=context) + {'self': self}, 'SEPA', gen_args) self.generate_party_block( - cr, uid, dd_transaction_info_2_28, 'Dbtr', 'C', + dd_transaction_info_2_28, 'Dbtr', 'C', 'line.partner_id.name', 'line.bank_id.acc_number', 'line.bank_id.bank.bic or ' 'line.bank_id.bank_bic', - {'line': line}, gen_args, context=context) + {'line': line}, gen_args) self.generate_remittance_info_block( - cr, uid, dd_transaction_info_2_28, - line, gen_args, context=context) + dd_transaction_info_2_28, line, gen_args) - nb_of_transactions_2_4.text = str(transactions_count_2_4) + nb_of_transactions_2_4.text = unicode(transactions_count_2_4) control_sum_2_5.text = '%.2f' % amount_control_sum_2_5 - nb_of_transactions_1_6.text = str(transactions_count_1_6) + nb_of_transactions_1_6.text = unicode(transactions_count_1_6) control_sum_1_7.text = '%.2f' % amount_control_sum_1_7 return self.finalize_sepa_file_creation( - cr, uid, ids, xml_root, total_amount, transactions_count_1_6, - gen_args, context=context) + xml_root, total_amount, transactions_count_1_6, gen_args) - def save_sepa(self, cr, uid, ids, context=None): + @api.multi + def save_sepa(self): """Save the SEPA Direct Debit file: mark all payments in the file as 'sent'. Write 'last debit date' on mandate and set oneoff mandate to expired. """ - sepa_export = self.browse(cr, uid, ids[0], context=context) - for order in sepa_export.payment_order_ids: - workflow.trg_validate(uid, 'payment.order', order.id, 'done', cr) - self.pool['ir.attachment'].create( - cr, uid, { - 'res_model': 'payment.order', - 'res_id': order.id, - 'name': sepa_export.filename, - 'datas': sepa_export.file, - }, context=context) - to_expire_ids = [] - first_mandate_ids = [] + abmo = self.env['account.banking.mandate'] + for order in self.payment_order_ids: + workflow.trg_validate( + self._uid, 'payment.order', order.id, 'done', self._cr) + self.env['ir.attachment'].create({ + 'res_model': 'payment.order', + 'res_id': order.id, + 'name': self.filename, + 'datas': self.file, + }) + to_expire_mandates = abmo.browse([]) + first_mandates = abmo.browse([]) + all_mandates = abmo.browse([]) for line in order.line_ids: + all_mandates += line.mandate_id if line.mandate_id.type == 'oneoff': - to_expire_ids.append(line.mandate_id.id) + to_expire_mandates += line.mandate_id elif line.mandate_id.type == 'recurrent': seq_type = line.mandate_id.recurrent_sequence_type if seq_type == 'final': - to_expire_ids.append(line.mandate_id.id) + to_expire_mandates += line.mandate_id elif seq_type == 'first': - first_mandate_ids.append(line.mandate_id.id) - self.pool['account.banking.mandate'].write( - cr, uid, to_expire_ids, {'state': 'expired'}, context=context) - self.pool['account.banking.mandate'].write( - cr, uid, first_mandate_ids, { - 'recurrent_sequence_type': 'recurring', - 'sepa_migrated': True, - }, context=context) + first_mandates += line.mandate_id + all_mandates.write( + {'last_debit_date': fields.Date.context_today(self)}) + to_expire_mandates.write({'state': 'expired'}) + first_mandates.write({ + 'recurrent_sequence_type': 'recurring', + 'sepa_migrated': True, + }) return True