[MIG] Migration and enhancement of all modules involved in SEPA

This commit is contained in:
Pedro M. Baeza
2014-09-10 12:48:09 +02:00
parent 15989cc9d0
commit aa54c740ca
180 changed files with 3395 additions and 368 deletions

View File

@@ -1 +0,0 @@
from . import model

View File

@@ -1 +0,0 @@
import model

View File

@@ -1,5 +0,0 @@
import account_payment
import payment_line
import account_move_line
import account_invoice
import payment_order_create

View File

@@ -1,2 +0,0 @@
# -*- coding: utf-8 -*-
from . import payment_order

View File

@@ -1,34 +0,0 @@
##############################################################################
#
# Copyright (C) 2011 Therp BV (<http://therp.nl>).
# 2011 Smile BV (<http://smile.fr>).
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Account Payment Invoice Selection Shortcut',
'version': '1.134',
'license': 'AGPL-3',
'author': 'Smile / Therp BV',
'website': 'https://launchpad.net/banking-addons',
'category': 'Banking addons',
'depends': ['account_payment'],
'description': '''
When composing a payment order, select all candidates by default
(in the second step of the "Select invoices to pay" wizard).
''',
'installable': False,
}

View File

@@ -1,54 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm
class payment_order_create(orm.TransientModel):
_inherit = 'payment.order.create'
def default_get(self, cr, uid, fields_list, context=None):
"""
Automatically add the candidate move lines to
the payment order, instead of only applying them
to the domain.
We make use of the fact that the search_entries
method passes an action without a res_id so that a
new instance is created. Inject the line_ids, which have
been placed in the context at object
creation time.
"""
if context is None:
context = {}
res = super(payment_order_create, self).default_get(
cr, uid, fields_list, context=context)
if (fields_list
and 'entries' in fields_list
and 'entries' not in res
and context.get('line_ids', False)):
res['entries'] = context['line_ids']
return res

View File

@@ -0,0 +1,23 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# PAIN Base module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import models

View File

@@ -26,15 +26,16 @@
'license': 'AGPL-3',
'author': 'Akretion, Noviat',
'website': 'http://openerp-community-association.org/',
'contributors': ['Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>'],
'category': 'Hidden',
'depends': ['account_banking_payment_export'],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
'data': [
'payment_line_view.xml',
'payment_mode_view.xml',
'company_view.xml',
'views/payment_line_view.xml',
'views/payment_mode_view.xml',
'views/res_company_view.xml',
],
'description': '''
Base module for PAIN file generation
@@ -43,14 +44,10 @@ Base module for PAIN file generation
This module contains fields and functions that are used by the module for SEPA
Credit Transfer (account_banking_sepa_credit_transfer) and SEPA Direct Debit
(account_banking_sepa_direct_debit). This module doesn't provide any
functionnality by itself.
functionality by itself.
This module is part of the banking addons:
https://www.github.com/OCA/banking-addons
This module was started during the Akretion-Noviat code sprint of
November 21st 2013 in Epiais les Louvres (France).
This module was started during the Akretion-Noviat code sprint of November
21st 2013 in Epiais les Louvres (France).
''',
'active': False,
'installable': False,
'installable': True,
}

View File

@@ -22,5 +22,5 @@
from . import payment_line
from . import payment_mode
from . import company
from . import res_company
from . import banking_export_pain

View File

@@ -0,0 +1,440 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# PAIN Base module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval
from datetime import datetime
from unidecode import unidecode
from lxml import etree
from openerp import tools
import logging
import base64
logger = logging.getLogger(__name__)
class BankingExportPain(orm.AbstractModel):
_name = 'banking.export.pain'
def _validate_iban(self, cr, uid, iban, context=None):
'''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):
return iban.replace(' ', '')
else:
raise orm.except_orm(
_('Error:'), _("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):
"""This function is designed to be inherited !"""
if gen_args is None:
gen_args = {}
assert isinstance(eval_ctx, dict), 'eval_ctx must contain a dict'
try:
value = safe_eval(field_value, eval_ctx)
# SEPA uses XML ; XML = UTF-8 ; UTF-8 = support for all characters
# But we are dealing with banks...
# and many banks don't want non-ASCCI characters !
# cf section 1.4 "Character set" of the SEPA Credit Transfer
# Scheme Customer-to-bank guidelines
if gen_args.get('convert_to_ascii'):
value = unidecode(value)
unallowed_ascii_chars = [
'"', '#', '$', '%', '&', '*', ';', '<', '>', '=', '@',
'[', ']', '^', '_', '`', '{', '}', '|', '~', '\\', '!']
for unallowed_ascii_char in unallowed_ascii_chars:
value = value.replace(unallowed_ascii_char, '-')
except:
line = eval_ctx.get('line')
if line:
raise orm.except_orm(
_('Error:'),
_("Cannot compute the '%s' of the Payment Line with "
"reference '%s'.")
% (field_name, line.name))
else:
raise orm.except_orm(
_('Error:'),
_("Cannot compute the '%s'.") % field_name)
if not isinstance(value, (str, unicode)):
raise orm.except_orm(
_('Field type error:'),
_("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:'),
_("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 _prepare_export_sepa(
self, cr, uid, total_amount, transactions_count, xml_string,
gen_args, context=None):
return {
'batch_booking': gen_args['sepa_export'].batch_booking,
'charge_bearer': gen_args['sepa_export'].charge_bearer,
'total_amount': total_amount,
'nb_transactions': transactions_count,
'file': base64.encodestring(xml_string),
'payment_order_ids': [(
6, 0, [x.id for x in gen_args['sepa_export'].payment_order_ids]
)],
}
def _validate_xml(self, cr, uid, xml_string, gen_args, context=None):
xsd_etree_obj = etree.parse(
tools.file_open(gen_args['pain_xsd_file']))
official_pain_schema = etree.XMLSchema(xsd_etree_obj)
try:
root_to_validate = etree.fromstring(xml_string)
official_pain_schema.assertValid(root_to_validate)
except Exception, e:
logger.warning(
"The XML file is invalid against the XML Schema Definition")
logger.warning(xml_string)
logger.warning(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 finalize_sepa_file_creation(
self, cr, uid, ids, xml_root, total_amount, transactions_count,
gen_args, context=None):
xml_string = etree.tostring(
xml_root, pretty_print=True, encoding='UTF-8',
xml_declaration=True)
logger.debug(
"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)
file_id = gen_args['file_obj'].create(
cr, uid, self._prepare_export_sepa(
cr, uid, total_amount, transactions_count,
xml_string, gen_args, context=context),
context=context)
self.write(
cr, uid, ids, {
'file_id': file_id,
'state': 'finish',
}, context=context)
action = {
'name': 'SEPA File',
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': self._name,
'res_id': ids[0],
'target': 'new',
}
return action
def generate_group_header_block(
self, cr, uid, parent_node, gen_args, context=None):
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)
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')
if gen_args.get('pain_flavor') == 'pain.001.001.02':
# 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()
nb_of_transactions_1_6 = etree.SubElement(
group_header_1_0, 'NbOfTxs')
control_sum_1_7 = etree.SubElement(group_header_1_0, 'CtrlSum')
# Grpg removed in pain.001.001.03
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)
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,
priority, local_instrument, sequence_type, requested_date,
eval_ctx, gen_args, context=None):
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_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()
# The "SEPA Customer-to-bank
# Implementation guidelines" for SCT and SDD says that control sum
# and nb_of_transactions should be present
# at both "group header" level and "payment info" level
nb_of_transactions_2_4 = etree.SubElement(
payment_info_2_0, 'NbOfTxs')
control_sum_2_5 = etree.SubElement(payment_info_2_0, 'CtrlSum')
payment_type_info_2_6 = etree.SubElement(
payment_info_2_0, 'PmtTpInf')
if priority:
instruction_priority_2_7 = etree.SubElement(
payment_type_info_2_6, 'InstrPrty')
instruction_priority_2_7.text = priority
service_level_2_8 = etree.SubElement(
payment_type_info_2_6, 'SvcLvl')
service_level_code_2_9 = etree.SubElement(service_level_2_8, 'Cd')
service_level_code_2_9.text = 'SEPA'
if local_instrument:
local_instrument_2_11 = etree.SubElement(
payment_type_info_2_6, 'LclInstrm')
local_instr_code_2_12 = etree.SubElement(
local_instrument_2_11, 'Cd')
local_instr_code_2_12.text = local_instrument
if sequence_type:
sequence_type_2_14 = etree.SubElement(
payment_type_info_2_6, 'SeqTp')
sequence_type_2_14.text = sequence_type
if gen_args['payment_method'] == 'DD':
request_date_tag = 'ReqdColltnDt'
else:
request_date_tag = 'ReqdExctnDt'
requested_date_2_17 = etree.SubElement(
payment_info_2_0, request_date_tag)
requested_date_2_17.text = requested_date
return payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5
def generate_initiating_party_block(
self, cr, uid, parent_node, gen_args, context=None):
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)
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 = self.pool['res.company'].\
_get_initiating_party_identifier(
cr, uid,
gen_args['sepa_export'].payment_order_ids[0].company_id.id,
context=context)
initiating_party_issuer = gen_args['sepa_export'].\
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')
iniparty_org_id = etree.SubElement(iniparty_id, 'OrgId')
iniparty_org_other = etree.SubElement(iniparty_org_id, 'Othr')
iniparty_org_other_id = etree.SubElement(iniparty_org_other, 'Id')
iniparty_org_other_id.text = initiating_party_identifier
iniparty_org_other_issuer = etree.SubElement(
iniparty_org_other, 'Issr')
iniparty_org_other_issuer.text = initiating_party_issuer
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):
'''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)
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:
if order == 'C':
if iban[0:2] != gen_args['initiating_party_country_code']:
raise orm.except_orm(
_('Error:'),
_("The bank account with IBAN '%s' of partner '%s' "
"must have an associated BIC because it is a "
"cross-border SEPA operation.")
% (iban, party_name))
if order == 'B' or (
order == 'C' and gen_args['payment_method'] == 'DD'):
party_agent = etree.SubElement(
parent_node, '%sAgt' % party_type)
party_agent_institution = etree.SubElement(
party_agent, 'FinInstnId')
party_agent_other = etree.SubElement(
party_agent_institution, 'Othr')
party_agent_other_identification = etree.SubElement(
party_agent_other, 'Id')
party_agent_other_identification.text = 'NOTPROVIDED'
# for Credit Transfers, in the 'C' block, if BIC is not provided,
# we should not put the 'Creditor Agent' block at all,
# as per the guidelines of the EPC
return True
def generate_party_block(
self, cr, uid, parent_node, party_type, order, name, iban, bic,
eval_ctx, gen_args, context=None):
'''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'"
if party_type == 'Cdtr':
party_type_label = 'Creditor'
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)
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)
# 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)
party = etree.SubElement(parent_node, party_type)
party_nm = etree.SubElement(party, 'Nm')
party_nm.text = party_name
party_account = etree.SubElement(
parent_node, '%sAcct' % party_type)
party_account_id = etree.SubElement(party_account, 'Id')
party_account_iban = etree.SubElement(
party_account_id, 'IBAN')
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)
return True
def generate_remittance_info_block(
self, cr, uid, parent_node, line, gen_args, context=None):
remittance_info_2_91 = etree.SubElement(
parent_node, 'RmtInf')
if line.state == 'normal':
remittance_info_unstructured_2_99 = etree.SubElement(
remittance_info_2_91, 'Ustrd')
remittance_info_unstructured_2_99.text = \
self._prepare_field(
cr, uid, 'Remittance Unstructured Information',
'line.communication', {'line': line}, 140,
gen_args=gen_args,
context=context)
else:
if not line.struct_communication_type:
raise orm.except_orm(
_('Error:'),
_("Missing 'Structured Communication Type' on payment "
"line with reference '%s'.")
% (line.name))
remittance_info_structured_2_100 = etree.SubElement(
remittance_info_2_91, 'Strd')
creditor_ref_information_2_120 = etree.SubElement(
remittance_info_structured_2_100, 'CdtrRefInf')
if gen_args.get('pain_flavor') == 'pain.001.001.02':
creditor_ref_info_type_2_121 = etree.SubElement(
creditor_ref_information_2_120, 'CdtrRefTp')
creditor_ref_info_type_code_2_123 = etree.SubElement(
creditor_ref_info_type_2_121, 'Cd')
creditor_ref_info_type_issuer_2_125 = etree.SubElement(
creditor_ref_info_type_2_121, 'Issr')
creditor_reference_2_126 = etree.SubElement(
creditor_ref_information_2_120, 'CdtrRef')
else:
creditor_ref_info_type_2_121 = etree.SubElement(
creditor_ref_information_2_120, 'Tp')
creditor_ref_info_type_or_2_122 = etree.SubElement(
creditor_ref_info_type_2_121, 'CdOrPrtry')
creditor_ref_info_type_code_2_123 = etree.SubElement(
creditor_ref_info_type_or_2_122, 'Cd')
creditor_ref_info_type_issuer_2_125 = etree.SubElement(
creditor_ref_info_type_2_121, 'Issr')
creditor_reference_2_126 = etree.SubElement(
creditor_ref_information_2_120, 'Ref')
creditor_ref_info_type_code_2_123.text = 'SCOR'
creditor_ref_info_type_issuer_2_125.text = \
line.struct_communication_type
creditor_reference_2_126.text = \
self._prepare_field(
cr, uid, 'Creditor Structured Reference',
'line.communication', {'line': line}, 35,
gen_args=gen_args,
context=context)
return True
def generate_creditor_scheme_identification(
self, cr, uid, parent_node, identification, identification_label,
eval_ctx, scheme_name_proprietary, gen_args, context=None):
csi_id = etree.SubElement(
parent_node, 'Id')
csi_privateid = csi_id = 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)
csi_scheme_name = etree.SubElement(csi_other, 'SchmeNm')
csi_scheme_name_proprietary = etree.SubElement(
csi_scheme_name, 'Prtry')
csi_scheme_name_proprietary.text = scheme_name_proprietary
return True

View File

@@ -0,0 +1,82 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# PAIN Base module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# Copyright (C) 2013 Noviat (http://www.noviat.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author: Luc de Meyer (Noviat)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class ResCompany(orm.Model):
_inherit = 'res.company'
_columns = {
'initiating_party_issuer': fields.char(
'Initiating Party Issuer', size=35,
help="This will be used as the 'Initiating Party Issuer' in the "
"PAIN files generated by OpenERP."),
}
def _get_initiating_party_identifier(
self, cr, uid, company_id, context=None):
"""The code here may be different from one country to another.
If you need to add support for an additionnal country, you can
contribute your code here or inherit this function in the
localization modules for your country"""
assert isinstance(company_id, int), 'Only one company ID'
company = self.browse(cr, uid, company_id, context=context)
company_vat = company.vat
party_identifier = False
if company_vat:
country_code = company_vat[0:2].upper()
if country_code == 'BE':
party_identifier = company_vat[2:].replace(' ', '')
elif country_code == 'ES':
party_identifier = company.sepa_creditor_identifier
return party_identifier
def _initiating_party_issuer_default(self, cr, uid, context=None):
"""If you need to add support for an additionnal country, you can
add an entry in the dict "party_issuer_per_country" here
or inherit this function in the localization modules for
your country"""
initiating_party_issuer = ''
# If your country require the 'Initiating Party Issuer', you should
# contribute the entry for your country in the dict below
party_issuer_per_country = {
'BE': 'KBO-BCE', # KBO-BCE = the registry of companies in Belgium
}
company_id = self._company_default_get(
cr, uid, 'res.company', context=context)
if company_id:
company = self.browse(cr, uid, company_id, context=context)
country_code = company.country_id.code
initiating_party_issuer = party_issuer_per_country.get(
country_code, '')
return initiating_party_issuer
def _initiating_party_issuer_def(self, cr, uid, context=None):
return self._initiating_party_issuer_default(
cr, uid, context=context)
_defaults = {
'initiating_party_issuer': _initiating_party_issuer_def,
}

View File

@@ -0,0 +1,52 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# PAIN Base module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class payment_line(orm.Model):
_inherit = 'payment.line'
def _get_struct_communication_types(self, cr, uid, context=None):
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',
}

View File

@@ -0,0 +1,39 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# PAIN Base module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class payment_mode(orm.Model):
_inherit = 'payment.mode'
_columns = {
'convert_to_ascii': fields.boolean(
'Convert to ASCII',
help="If active, OpenERP 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,
}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_payment_line_form" model="ir.ui.view">
<field name="name">pain.base.payment.line.form</field>
<field name="model">payment.line</field>
<field name="inherit_id" ref="account_payment.view_payment_line_form"/>
<field name="arch" type="xml">
<field name="bank_id" position="after">
<field name="priority"/>
<newline />
</field>
<field name="state" position="after">
<field name="struct_communication_type" attrs="{'invisible': [('state', '!=', 'structured')], 'required': [('state', '=', 'structured')]}"/>
</field>
</field>
</record>
<record id="view_payment_order_form" model="ir.ui.view">
<field name="name">pain.base.payment.line.inside.order.form</field>
<field name="model">payment.order</field>
<field name="inherit_id" ref="account_payment.view_payment_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']/form//field[@name='bank_id']" position="after">
<field name="priority"/>
<newline />
</xpath>
<xpath expr="//field[@name='line_ids']/form//field[@name='state']" position="after">
<field name="struct_communication_type" attrs="{'invisible': [('state', '!=', 'structured')], 'required': [('state', '=', 'structured')]}"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
<field name="name">add.convert_to_ascii.in.payment.mode.form</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_banking_payment_export.view_payment_mode_form_inherit"/>
<field name="arch" type="xml">
<field name="type" position="after">
<field name="convert_to_ascii"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_company_form" model="ir.ui.view">
<field name="name">pain.group.on.res.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<group name="account_grp" position="after">
<group name="pain" string="Payment Initiation">
<field name="initiating_party_issuer"/>
</group>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard

View File

@@ -47,43 +47,41 @@
],
'data': [
'view/account_payment.xml',
'view/bank_payment_manual.xml',
'view/payment_mode.xml',
'view/payment_mode_type.xml',
'view/payment_order_create_view.xml',
'wizard/bank_payment_manual.xml',
'wizard/payment_order_create_view.xml',
'data/payment_mode_type.xml',
'security/ir.model.access.csv',
],
'demo': ['demo/banking_demo.xml'],
'description': '''
Infrastructure to export payment orders
plus some bug fixes and obvious enhancements to payment orders
that will hopefully land in offical addons one day.
Infrastructure to export payment orders plus some bug fixes and obvious
enhancements to payment orders that will hopefully land in offical addons one
day.
This technical module provides the base infrastructure to export
payment orders for electronic banking. It provides the following
technical features:
* a new payment.mode.type model
* payment.mode now has a mandatory type
* a better implementation of payment_mode.suitable_bank_types() based
on payment.mode.type
* the "make payment" button launches a wizard depending on the
payment.mode.type
* a manual payment mode type is provided as an example, with a default
"do nothing" wizard
This technical module provides the base infrastructure to export payment orders
for electronic banking. It provides the following technical features:
To enable the use of payment order to collect money for customers,
it adds a payment_order_type (payment|debit) as a basis of direct debit
support (this field becomes visible when account_direct_debit is
installed).
Refactoring note: this field should ideally go in account_direct_debit,
but account_banking_payment currently depends on it.
* a new payment.mode.type model
* payment.mode now has a mandatory type
* a better implementation of payment_mode.suitable_bank_types() based on
payment.mode.type
* the "make payment" button launches a wizard depending on the
payment.mode.type
* a manual payment mode type is provided as an example, with a default "do
nothing" wizard
Bug fixes and enhancement that should land in official addons:
* make the search function of the payment export wizard extensible
* fix lp:1275478: allow payment of customer refunds
* display the maturity date of the move lines when you are in
the wizard to select the lines to pay
''',
'installable': False,
To enable the use of payment order to collect money for customers,
it adds a payment_order_type (payment|debit) as a basis of direct debit support
(this field becomes visible when account_direct_debit is installed).
Refactoring note: this field should ideally go in account_direct_debit,
but account_banking_payment currently depends on it.
Bug fixes and enhancement that should land in official addons:
* make the search function of the payment export wizard extensible
* fix lp:1275478: allow payment of customer refunds
''',
'installable': True,
}

View File

@@ -57,6 +57,10 @@
<field name="bank_bic">FTNOFRP1XXX</field>
</record>
<record id="account_payment.payment_mode_1" model="payment.mode">
<field name="type" ref="account_banking_payment_export.manual_bank_tranfer"/>
</record>
<record id="payment_mode_2" model="payment.mode">
<field name="name">Credit Trf Banque Postale</field>
<field name="journal" ref="account.bank_journal"/>

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import account_payment
from . import payment_mode
from . import payment_mode_type
from . import account_move_line

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2004-2014 OpenERP S.A. (http://www.openerp.com/)
# (C) 2014 Akretion (http://www.akretion.com/)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from operator import itemgetter
# All the code below aims at fixing one small issue in _to_pay_search()
# But _to_pay_search() is the search function of the field 'amount_to_pay'
# which is a field.function and these functions are not inheritable in OpenERP.
# So we have to inherit the field 'amount_to_pay' and duplicate the related
# functions
# If the patch that I proposed in this bug report
# https://bugs.launchpad.net/openobject-addons/+bug/1275478
# is integrated in addons/account_payment, then we will be able to remove this
# file. -- Alexis de Lattre
class AccountMoveLine(orm.Model):
_inherit = 'account.move.line'
def _amount_to_pay(self, cr, uid, ids, name, arg=None, context=None):
""" Return the amount still to pay regarding all the payemnt orders
(excepting cancelled orders)"""
if not ids:
return {}
cr.execute("""SELECT ml.id,
CASE WHEN ml.amount_currency < 0
THEN - ml.amount_currency
ELSE ml.credit
END -
(SELECT coalesce(sum(amount_currency),0)
FROM payment_line pl
INNER JOIN payment_order po
ON (pl.order_id = po.id)
WHERE move_line_id = ml.id
AND po.state != 'cancel') AS amount
FROM account_move_line ml
WHERE id IN %s""", (tuple(ids),))
r = dict(cr.fetchall())
return r
def _to_pay_search(self, cr, uid, obj, name, args, context=None):
if not args:
return []
line_obj = self.pool.get('account.move.line')
query = line_obj._query_get(cr, uid, context={})
where = ' and '.join(map(lambda x: '''(SELECT
CASE WHEN l.amount_currency < 0
THEN - l.amount_currency
ELSE l.credit
END - coalesce(sum(pl.amount_currency), 0)
FROM payment_line pl
INNER JOIN payment_order po ON (pl.order_id = po.id)
WHERE move_line_id = l.id
AND po.state != 'cancel'
) %(operator)s %%s ''' % {'operator': x[1]}, args))
sql_args = tuple(map(itemgetter(2), args))
cr.execute(
'''SELECT id
FROM account_move_line l
WHERE account_id IN (select id
FROM account_account
WHERE type in %s AND active)
AND reconcile_id IS null
AND credit > 0
AND ''' + where + ' and ' + query,
(('payable', 'receivable'),) + sql_args)
# The patch we have compared to the original function in
# addons/account_payment is just above :
# original code : type = 'payable'
# fixed code : type in ('payable', 'receivable')
res = cr.fetchall()
if not res:
return [('id', '=', '0')]
return [('id', 'in', map(lambda x:x[0], res))]
_columns = {
'amount_to_pay': fields.function(
_amount_to_pay, type='float', string='Amount to pay',
fnct_search=_to_pay_search),
}

View File

@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api, exceptions, _
from openerp import netsvc
class PaymentOrder(models.Model):
_inherit = 'payment.order'
payment_order_type = fields.Selection(
[('payment', 'Payment'), ('debit', 'Direct debit')],
'Payment order type', required=True, default='payment',
readonly=True, states={'draft': [('readonly', False)]})
mode_type = fields.Many2one('payment.mode.type', related='mode.type',
string='Payment Type')
@api.multi
def launch_wizard(self):
"""Search for a wizard to launch according to the type.
If type is manual. just confirm the order.
Previously (pre-v6) in account_payment/wizard/wizard_pay.py
"""
context = self.env.context.copy()
order = self[0]
# check if a wizard is defined for the first order
if order.mode.type and order.mode.type.ir_model_id:
context['active_ids'] = self.ids
wizard_model = order.mode.type.ir_model_id.model
wizard_obj = self.env[wizard_model]
return {
'name': wizard_obj._description or _('Payment Order Export'),
'view_type': 'form',
'view_mode': 'form',
'res_model': wizard_model,
'domain': [],
'context': context,
'type': 'ir.actions.act_window',
'target': 'new',
'nodestroy': True,
}
else:
# should all be manual orders without type or wizard model
for order in self[1:]:
if order.mode.type and order.mode.type.ir_model_id:
raise exceptions.Warning(
_('Error'),
_('You can only combine payment orders of the same '
'type'))
# process manual payments
wf_service = netsvc.LocalService('workflow')
for order_id in self.ids:
wf_service.trg_validate(self.env.uid, 'payment.order',
order_id, 'done', self.env.cr)
return {}

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields
class PaymentMode(models.Model):
"""Restoring the payment type from version 5,
used to select the export wizard (if any)
"""
_inherit = "payment.mode"
def suitable_bank_types(self, cr, uid, payment_mode_id=None, context=None):
""" Reinstates functional code for suitable bank type filtering.
Current code in account_payment is disfunctional.
"""
res = []
payment_mode = self.browse(cr, uid, payment_mode_id, context=context)
if (payment_mode and payment_mode.type and
payment_mode.type.suitable_bank_types):
res = [t.code for t in payment_mode.type.suitable_bank_types]
return res
type = fields.Many2one(
'payment.mode.type', string='Export type', required=True,
help='Select the Export Payment Type for the Payment Mode.')
payment_order_type = fields.Selection(
related='type.payment_order_type', readonly=True, string="Order Type",
selection=[('payment', 'Payment'), ('debit', 'Debit')],
help="This field, that comes from export type, determines if this "
"mode can be selected for customers or suppliers.")
active = fields.Boolean(string='Active', default=True)

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields
class PaymentModeType(models.Model):
_name = 'payment.mode.type'
_description = 'Payment Mode Type'
name = fields.Char('Name', size=64, required=True, help='Payment Type')
code = fields.Char('Code', size=64, required=True,
help='Specify the Code for Payment Type')
suitable_bank_types = fields.Many2many(
comodel_name='res.partner.bank.type',
relation='bank_type_payment_type_rel', column1='pay_type_id',
column2='bank_type_id', string='Suitable bank types', required=True)
ir_model_id = fields.Many2one(
'ir.model', string='Payment wizard',
help='Select the Payment Wizard for payments of this type. Leave '
'empty for manual processing',
domain=[('osv_memory', '=', True)])
payment_order_type = fields.Selection(
[('payment', 'Payment'), ('debit', 'Debit')],
string='Order type', required=True, default='payment',
help="This field determines if this type applies to customers "
"(Debit) or suppliers (Payment)")
active = fields.Boolean(string='Active', default=True)
def _auto_init(self, cr, context=None):
res = super(PaymentModeType, self)._auto_init(cr, context=context)
# migrate xmlid from manual_bank_transfer to avoid dependency on
# account_banking
cr.execute(
"""UPDATE ir_model_data
SET module='account_banking_payment_export'
WHERE module='account_banking' AND
name='manual_bank_tranfer' AND
model='payment.mode.type'""")
return res

View File

@@ -2,7 +2,7 @@
<openerp>
<data>
<!--
<!--
Add the payment mode type and transfer settings
-->
<record id="view_payment_mode_form_inherit" model="ir.ui.view">

View File

@@ -20,11 +20,18 @@
<field name="arch" type="xml">
<form string="Payment Type" version="7.0">
<group name="main">
<field name="name"/>
<field name="code"/>
<field name="active"/>
<field name="ir_model_id"/>
<field name="suitable_bank_types"/>
<group>
<field name="name"/>
<field name="ir_model_id"/>
</group>
<group>
<field name="code"/>
<field name="active"/>
</group>
</group>
<group string="Suitable bank types">
<field name="suitable_bank_types"
nolabel="1"/>
</group>
</form>
</field>
@@ -44,7 +51,7 @@
</record>
<record id="action_payment_mode_type" model="ir.actions.act_window">
<field name="name">Payment Type</field>
<field name="name">Payment Export Types</field>
<field name="res_model">payment.mode.type</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import payment_order_create
from . import bank_payment_manual

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
"""This module contains a single "wizard" for confirming manual
bank transfers.
"""
from openerp import models, fields, api
from openerp import netsvc
class PaymentManual(models.TransientModel):
_name = 'payment.manual'
_description = 'Send payment order(s) manually'
payment_order_ids = fields.Many2many(
comodel_name='payment.order', relation='wiz_manual_payorders_rel',
column1='wizard_id', column2='payment_order_id',
string='Payment orders', readonly=True),
def create(self, vals):
payment_order_ids = self.env.context.get('active_ids', [])
vals['payment_order_ids'] = [[6, 0, payment_order_ids]]
return super(PaymentManual, self).create(vals)
@api.one
def button_ok(self):
wf_service = netsvc.LocalService('workflow')
for order_id in self.payment_order_ids:
wf_service.trg_validate(self.env.uid, 'payment.order', order_id.id,
'done', self.env.cr)
return {'type': 'ir.actions.act_window_close'}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_payment_manual_form" model="ir.ui.view">
<field name="name">Form for manual payment wizard</field>
<field name="model">payment.manual</field>
<field name="arch" type="xml">
<form string="Manual payment" version="7.0">
<label string="Please execute payment order manually, and click OK when succesfully sent."/>
<footer>
<button name="button_ok" type="object" string="OK" class="oe_highlight"/>
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api, _
class PaymentOrderCreate(models.TransientModel):
_inherit = 'payment.order.create'
populate_results = fields.Boolean(string="Populate results directly",
default=True)
@api.model
def default_get(self, fields):
res = super(PaymentOrderCreate, self).default_get(fields)
context = self.env.context
if ('entries' in fields and context.get('line_ids') and
context.get('populate_results')):
res.update({'entries': context['line_ids']})
return res
@api.model
def extend_payment_order_domain(self, payment_order, domain):
if payment_order.payment_order_type == 'payment':
domain += [('account_id.type', 'in', ('payable', 'receivable')),
('amount_to_pay', '>', 0)]
return True
@api.multi
def search_entries(self):
"""This method taken from account_payment module.
We adapt the domain based on the payment_order_type
"""
line_obj = self.env['account.move.line']
model_data_obj = self.env['ir.model.data']
# -- start account_banking_payment --
payment = self.env['payment.order'].browse(
self.env.context['active_id'])
# Search for move line to pay:
domain = [('move_id.state', '=', 'posted'),
('reconcile_id', '=', False),
('company_id', '=', payment.mode.company_id.id)]
self.extend_payment_order_domain(payment, domain)
# -- end account_direct_debit --
domain += ['|',
('date_maturity', '<=', self.duedate),
('date_maturity', '=', False)]
lines = line_obj.search(domain)
context = self.env.context.copy()
context['line_ids'] = lines.ids
context['populate_results'] = self.populate_results
model_datas = model_data_obj.search(
[('model', '=', 'ir.ui.view'),
('name', '=', 'view_create_payment_order_lines')])
return {'name': _('Entry Lines'),
'context': context,
'view_type': 'form',
'view_mode': 'form',
'res_model': 'payment.order.create',
'views': [(model_datas[0].res_id, 'form')],
'type': 'ir.actions.act_window',
'target': 'new',
}
@api.model
def _prepare_payment_line(self, payment, line):
"""This function is designed to be inherited
The resulting dict is passed to the create method of payment.line"""
_today = fields.Date.context_today(self)
date_to_pay = False # no payment date => immediate payment
if payment.date_prefered == 'due':
# -- account_banking
# date_to_pay = line.date_maturity
date_to_pay = (
line.date_maturity
if line.date_maturity and line.date_maturity > _today
else False)
# -- end account banking
elif payment.date_prefered == 'fixed':
# -- account_banking
# date_to_pay = payment.date_scheduled
date_to_pay = (
payment.date_scheduled
if payment.date_scheduled and payment.date_scheduled > _today
else False)
# -- end account banking
# -- account_banking
state = 'normal'
communication = line.ref or '-'
if line.invoice:
if line.invoice.type in ('in_invoice', 'in_refund'):
if line.invoice.reference_type == 'structured':
state = 'structured'
communication = line.invoice.reference
else:
if line.invoice.reference:
communication = line.invoice.reference
elif line.invoice.supplier_invoice_number:
communication = line.invoice.supplier_invoice_number
else:
# Make sure that the communication includes the
# customer invoice number (in the case of debit order)
communication = line.invoice.number.replace('/', '')
state = 'structured'
# support debit orders when enabled
if (payment.payment_order_type == 'debit' and
'amount_to_receive' in line):
amount_currency = line.amount_to_receive
else:
amount_currency = line.amount_to_pay
line2bank = line.line2bank(payment.mode.id)
# -- end account banking
res = {'move_line_id': line.id,
'amount_currency': amount_currency,
'bank_id': line2bank.get(line.id),
'order_id': payment.id,
'partner_id': line.partner_id and line.partner_id.id or False,
# account banking
'communication': communication,
'state': state,
# end account banking
'date': date_to_pay,
'currency': (line.invoice and line.invoice.currency_id.id
or line.journal_id.currency.id
or line.journal_id.company_id.currency_id.id)}
return res
@api.multi
def create_payment(self):
"""This method is a slightly modified version of the existing method on
this model in account_payment.
- pass the payment mode to line2bank()
- allow invoices to create influence on the payment process: not only
'Free' references are allowed, but others as well
- check date_to_pay is not in the past.
"""
if not self.entries:
return {'type': 'ir.actions.act_window_close'}
context = self.env.context
payment_line_obj = self.env['payment.line']
payment = self.env['payment.order'].browse(context['active_id'])
# Populate the current payment with new lines:
for line in self.entries:
vals = self._prepare_payment_line(payment, line)
payment_line_obj.create(vals)
# Force reload of payment order view as a workaround for lp:1155525
return {'name': _('Payment Orders'),
'context': context,
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': 'payment.order',
'res_id': context['active_id'],
'type': 'ir.actions.act_window'}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_create_payment_order" model="ir.ui.view">
<field name="name">payment.order.create.form.export</field>
<field name="model">payment.order.create</field>
<field name="inherit_id" ref="account_payment.view_create_payment_order"/>
<field name="arch" type="xml">
<field name="duedate" position="after">
<field name="populate_results"/>
</field>
</field>
</record>
<record id="view_create_payment_order_lines" model="ir.ui.view">
<field name="name">add.context.to.display.maturity.date</field>
<field name="model">payment.order.create</field>
<field name="inherit_id" ref="account_payment.view_create_payment_order_lines"/>
<field name="arch" type="xml">
<field name="entries" position="attributes">
<attribute name="context">{'journal_type': 'sale'}</attribute>
<attribute name="nolabel">1</attribute>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,24 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Credit Transfer module for OpenERP
# Copyright (C) 2010-2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import wizard
from . import models

View File

@@ -26,39 +26,34 @@
'license': 'AGPL-3',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'contributors': ['Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>'],
'category': 'Banking addons',
'depends': ['account_banking_pain_base'],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
},
'data': [
'account_banking_sepa_view.xml',
'views/account_banking_sepa_view.xml',
'wizard/export_sepa_view.xml',
'data/payment_type_sepa_sct.xml',
'security/ir.model.access.csv',
],
'demo': ['sepa_credit_transfer_demo.xml'],
'demo': [
'demo/sepa_credit_transfer_demo.xml'
],
'description': '''
Module to export payment orders in SEPA XML file format.
SEPA PAIN (PAyment INitiation) is the new european standard for
Customer-to-Bank payment instructions.
This module implements SEPA Credit Transfer (SCT), more specifically PAIN
versions 001.001.02, 001.001.03, 001.001.04 and 001.001.05.
It is part of the ISO 20022 standard, available on http://www.iso20022.org.
Customer-to-Bank payment instructions. This module implements SEPA Credit
Transfer (SCT), more specifically PAIN versions 001.001.02, 001.001.03,
001.001.04 and 001.001.05. It is part of the ISO 20022 standard, available on
http://www.iso20022.org.
The Implementation Guidelines for SEPA Credit Transfer published by the
European Payments Council (http://http://www.europeanpaymentscouncil.eu)
use PAIN version 001.001.03, so it's probably the version of PAIN that you
should try first.
This module uses the framework provided by the banking addons,
cf https://www.github.com/OCA/banking-addons
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com>
for any help or question about this module.
European Payments Council (http://http://www.europeanpaymentscouncil.eu) use
PAIN version 001.001.03, so it's probably the version of PAIN that you should
try first.
''',
'active': False,
'installable': False,
'installable': True,
}

View File

@@ -7,6 +7,7 @@
<record id="export_sepa_sct_001_001_05" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v05</field>
<field name="code">pain.001.001.05</field>
<field name="payment_order_type">payment</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
@@ -15,6 +16,7 @@
<record id="export_sepa_sct_001_001_04" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v04</field>
<field name="code">pain.001.001.04</field>
<field name="payment_order_type">payment</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
@@ -23,6 +25,7 @@
<record id="export_sepa_sct_001_001_03" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v03 (recommended)</field>
<field name="code">pain.001.001.03</field>
<field name="payment_order_type">payment</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
@@ -31,6 +34,7 @@
<record id="export_sepa_sct_001_001_02" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v02</field>
<field name="code">pain.001.001.02</field>
<field name="payment_order_type">payment</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>

View File

@@ -20,5 +20,4 @@
#
##############################################################################
from . import wizard
from . import account_banking_sepa

View File

@@ -0,0 +1,76 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Credit Transfer module for OpenERP
# Copyright (C) 2010-2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
from openerp.addons.decimal_precision import decimal_precision as dp
from unidecode import unidecode
class BankingExportSepa(models.Model):
"""SEPA export"""
_name = 'banking.export.sepa'
_description = __doc__
_rec_name = 'filename'
@api.one
def _generate_filename(self):
ref = self.payment_order_ids[0].reference
if ref:
label = unidecode(ref.replace('/', '-'))
else:
label = 'error'
self.filename = 'sct_%s.xml' % label
payment_order_ids = fields.Many2many(
comodel_name='payment.order', column1='banking_export_sepa_id',
column2='account_order_id', relation='account_payment_order_sepa_rel',
string='Payment Orders', readonly=True)
nb_transactions = fields.Integer(string='Number of Transactions',
readonly=True)
total_amount = fields.Float(string='Total Amount',
digits_compute=dp.get_precision('Account'),
readonly=True)
batch_booking = fields.Boolean(
'Batch Booking', readonly=True,
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', readonly=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.")
create_date = fields.Datetime('Generation Date', readonly=True)
file = fields.Binary('SEPA XML File', readonly=True)
filename = fields.Char(string='Filename', size=256, readonly=True,
compute=_generate_filename)
state = fields.Selection([('draft', 'Draft'), ('sent', 'Sent')],
string='State', readonly=True, default='draft')

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="sepa_credit_transfer_mode" model="payment.mode">
<field name="name">SEPA Credit Transfer La Banque Postale</field>
<field name="journal" ref="account.bank_journal"/>
<field name="bank_id" ref="account_banking_payment_export.main_company_iban"/>
<field name="company_id" ref="base.main_company"/>
<field name="type" ref="export_sepa_sct_001_001_03"/>
</record>
</data>
</openerp>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010-2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_banking_export_sepa_form" model="ir.ui.view">
<field name="name">account.banking.export.sepa.form</field>
<field name="model">banking.export.sepa</field>
<field name="arch" type="xml">
<form string="SEPA Credit Transfer" version="7.0">
<header>
<field name="state" widget="statusbar"/>
</header>
<notebook>
<page string="General Information">
<group name="main">
<field name="total_amount" />
<field name="nb_transactions" />
<field name="batch_booking" />
<field name="charge_bearer"/>
<field name="create_date" />
<field name="file" filename="filename"/>
<field name="filename" invisible="True"/>
</group>
</page>
<page string="Payment Orders">
<field name="payment_order_ids" nolabel="1"/>
</page>
</notebook>
</form>
</field>
</record>
<record id="view_banking_export_sepa_tree" model="ir.ui.view">
<field name="name">account.banking.export.sepa.tree</field>
<field name="model">banking.export.sepa</field>
<field name="arch" type="xml">
<tree string="SEPA Credit Transfer">
<field name="filename"/>
<field name="create_date"/>
<field name="nb_transactions"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="action_account_banking_sepa" model="ir.actions.act_window">
<field name="name">SEPA Credit Transfer Files</field>
<field name="res_model">banking.export.sepa</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_account_banking_sepa"
parent="account_payment.menu_main_payment"
action="action_account_banking_sepa"
sequence="15"
/>
<act_window id="act_banking_export_sepa_payment_order"
name="SEPA Credit Transfer Files"
domain="[('payment_order_ids', '=', active_id)]"
res_model="banking.export.sepa"
src_model="payment.order"
view_type="form"
view_mode="tree,form"
/>
</data>
</openerp>

View File

@@ -89,9 +89,7 @@ class banking_export_sepa_wizard(orm.TransientModel):
cr, uid, vals, context=context)
def create_sepa(self, cr, uid, ids, context=None):
'''
Creates the SEPA Credit Transfer file. That's the important code !
'''
"""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)
@@ -122,16 +120,14 @@ class banking_export_sepa_wizard(orm.TransientModel):
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrCdtTrfInitn'
else:
raise orm.except_orm(
_('Error:'),
_("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', "
"'pain.001.001.04' and 'pain.001.001.05'.")
% pain_flavor)
"Payment Type Codes supported for SEPA Credit Transfers "
"are 'pain.001.001.02', 'pain.001.001.03', "
"'pain.001.001.04' and 'pain.001.001.05'.") %
pain_flavor)
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
@@ -144,22 +140,18 @@ class banking_export_sepa_wizard(orm.TransientModel):
'account_banking_sepa_credit_transfer/data/%s.xsd'
% pain_flavor,
}
pain_ns = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
None: 'urn:iso:std:iso:20022:tech:xsd:%s' % pain_flavor,
}
xml_root = etree.Element('Document', nsmap=pain_ns)
pain_root = etree.SubElement(xml_root, root_xml_tag)
pain_03_to_05 = \
['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)
transactions_count_1_6 = 0
total_amount = 0.0
amount_control_sum_1_7 = 0.0
@@ -187,7 +179,6 @@ class banking_export_sepa_wizard(orm.TransientModel):
self.pool['payment.line'].write(
cr, uid, line.id,
{'date': requested_date}, context=context)
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 = \
@@ -200,7 +191,6 @@ class banking_export_sepa_wizard(orm.TransientModel):
'priority': priority,
'requested_date': requested_date,
}, gen_args, context=context)
self.generate_party_block(
cr, uid, payment_info_2_0, 'Dbtr', 'B',
'sepa_export.payment_order_ids[0].mode.bank_id.partner_id.'
@@ -209,10 +199,8 @@ class banking_export_sepa_wizard(orm.TransientModel):
'sepa_export.payment_order_ids[0].mode.bank_id.bank.bic',
{'sepa_export': sepa_export},
gen_args, context=context)
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
charge_bearer_2_24.text = sepa_export.charge_bearer
transactions_count_2_4 = 0
amount_control_sum_2_5 = 0.0
for line in lines:
@@ -240,7 +228,6 @@ class banking_export_sepa_wizard(orm.TransientModel):
instructed_amount_2_43.text = '%.2f' % line.amount_currency
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:'),
@@ -252,45 +239,38 @@ class banking_export_sepa_wizard(orm.TransientModel):
'C', 'line.partner_id.name', 'line.bank_id.acc_number',
'line.bank_id.bank.bic', {'line': line}, gen_args,
context=context)
self.generate_remittance_info_block(
cr, uid, credit_transfer_transaction_info_2_27,
line, gen_args, context=context)
if pain_flavor in pain_03_to_05:
nb_of_transactions_2_4.text = str(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)
control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
else:
nb_of_transactions_1_6.text = str(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)
def cancel_sepa(self, cr, uid, ids, context=None):
'''
Cancel the SEPA file: just drop the file
'''
"""Cancel the SEPA file: just drop the file"""
sepa_export = self.browse(cr, uid, ids[0], context=context)
self.pool.get('banking.export.sepa').unlink(
self.pool['banking.export.sepa'].unlink(
cr, uid, sepa_export.file_id.id, context=context)
return {'type': 'ir.actions.act_window_close'}
def save_sepa(self, cr, uid, ids, context=None):
'''
Save the SEPA file: send the done signal to all payment
"""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)
self.pool.get('banking.export.sepa').write(
self.pool['banking.export.sepa'].write(
cr, uid, sepa_export.file_id.id, {'state': 'sent'},
context=context)
wf_service = netsvc.LocalService('workflow')

View File

@@ -20,6 +20,5 @@
#
##############################################################################
from . import company
from . import models
from . import wizard
from . import account_banking_sdd

View File

@@ -26,47 +26,40 @@
'license': 'AGPL-3',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'contributors': ['Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>'],
'category': 'Banking addons',
'depends': ['account_direct_debit', 'account_banking_pain_base'],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
'data': [
'security/original_mandate_required_security.xml',
'account_banking_sdd_view.xml',
'sdd_mandate_view.xml',
'res_partner_bank_view.xml',
'account_payment_view.xml',
'company_view.xml',
'mandate_expire_cron.xml',
'account_invoice_view.xml',
'views/account_banking_sdd_view.xml',
'views/sdd_mandate_view.xml',
'views/res_partner_bank_view.xml',
'views/account_payment_view.xml',
'views/res_company_view.xml',
'views/account_invoice_view.xml',
'wizard/export_sdd_view.xml',
'data/mandate_expire_cron.xml',
'data/payment_type_sdd.xml',
'data/mandate_reference_sequence.xml',
'security/original_mandate_required_security.xml',
'security/ir.model.access.csv',
],
'demo': ['sepa_direct_debit_demo.xml'],
'demo': ['demo/sepa_direct_debit_demo.xml'],
'description': '''
Module to export direct debit payment orders in SEPA XML file format.
SEPA PAIN (PAyment INitiation) is the new european standard for
Customer-to-Bank payment instructions.
This module implements SEPA Direct Debit (SDD), more specifically PAIN
versions 008.001.02, 008.001.03 and 008.001.04.
It is part of the ISO 20022 standard, available on http://www.iso20022.org.
Customer-to-Bank payment instructions. This module implements SEPA Direct
Debit (SDD), more specifically PAIN versions 008.001.02, 008.001.03 and
008.001.04. It is part of the ISO 20022 standard, available on
http://www.iso20022.org.
The Implementation Guidelines for SEPA Direct Debit published by the European
Payments Council (http://http://www.europeanpaymentscouncil.eu) use PAIN
version 008.001.02. So if you don't know which version your bank supports,
you should try version 008.001.02 first.
This module uses the framework provided by the banking addons,
cf https://www.github.com/OCA/banking-addons
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com>
for any help or question about this module.
version 008.001.02. So if you don't know which version your bank supports, you
should try version 008.001.02 first.
''',
'active': False,
'installable': False,
'installable': True,
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data noupdate="1"> <!-- noupdate = 1 for the 'active' field -->
<record id="sdd_mandate_expire_cron" model="ir.cron">
<field name="name">Set SEPA Direct Debit Mandates to Expired</field>
<field name="active" eval="True"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field> <!-- don't limit the number of calls -->
<field name="doall" eval="False"/>
<field name="model" eval="'sdd.mandate'"/>
<field name="function" eval="'_sdd_mandate_set_state_to_expired'" />
<field name="args" eval="'()'"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import account_invoice
from . import banking_export_sdd
from . import payment_line
from . import res_company
from . import res_partner_bank
from . import sdd_mandate

View File

@@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
sdd_mandate_id = fields.Many2one(
'sdd.mandate', string='SEPA Direct Debit Mandate',
domain=[('state', '=', 'valid')], readonly=True,
states={'draft': [('readonly', False)]})

Some files were not shown because too many files have changed in this diff Show More